mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
Merge pull request #367 from incubateur-territoires/translation-key-migration
Translation key migration
This commit is contained in:
commit
de1001f46d
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
|||||||
/landing.db
|
/landing.db
|
||||||
/docs/
|
/docs/
|
||||||
/sandbox_venv*
|
/sandbox_venv*
|
||||||
|
/.vscode/
|
||||||
|
|
||||||
# Build helper files.
|
# Build helper files.
|
||||||
/.build*
|
/.build*
|
||||||
|
@ -15,7 +15,7 @@ import {cssMenu, cssMenuWrap, defaultMenuOptions, IPopupOptions, setPopupToCreat
|
|||||||
import {getUserRoleText} from 'app/common/UserAPI';
|
import {getUserRoleText} from 'app/common/UserAPI';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('aclui.ViewAsDropdown');
|
const t = makeT("ACLUsers");
|
||||||
|
|
||||||
function isSpecialEmail(email: string) {
|
function isSpecialEmail(email: string) {
|
||||||
return email === ANONYMOUS_USER_EMAIL || email === EVERYONE_EMAIL;
|
return email === ANONYMOUS_USER_EMAIL || email === EVERYONE_EMAIL;
|
||||||
@ -54,15 +54,15 @@ export class ACLUsersPopup extends Disposable {
|
|||||||
return cssMenuWrap(cssMenu(
|
return cssMenuWrap(cssMenu(
|
||||||
dom.cls(menuCssClass),
|
dom.cls(menuCssClass),
|
||||||
cssUsers.cls(''),
|
cssUsers.cls(''),
|
||||||
cssHeader(t('ViewAs'), dom.show(this._shareUsers.length > 0)),
|
cssHeader(t('View As'), dom.show(this._shareUsers.length > 0)),
|
||||||
dom.forEach(this._shareUsers, buildRow),
|
dom.forEach(this._shareUsers, buildRow),
|
||||||
(this._attributeTableUsers.length > 0) ? cssHeader(t('UsersFrom')) : null,
|
(this._attributeTableUsers.length > 0) ? cssHeader(t("Users from table")) : null,
|
||||||
dom.forEach(this._attributeTableUsers, buildExampleUserRow),
|
dom.forEach(this._attributeTableUsers, buildExampleUserRow),
|
||||||
// Include example users only if there are not many "real" users.
|
// Include example users only if there are not many "real" users.
|
||||||
// It might be better to have an expandable section with these users, collapsed
|
// It might be better to have an expandable section with these users, collapsed
|
||||||
// by default, but that's beyond my UI ken.
|
// by default, but that's beyond my UI ken.
|
||||||
(this._shareUsers.length + this._attributeTableUsers.length < 5) ? [
|
(this._shareUsers.length + this._attributeTableUsers.length < 5) ? [
|
||||||
(this._exampleUsers.length > 0) ? cssHeader(t('ExampleUsers')) : null,
|
(this._exampleUsers.length > 0) ? cssHeader(t("Example Users")) : null,
|
||||||
dom.forEach(this._exampleUsers, buildExampleUserRow)
|
dom.forEach(this._exampleUsers, buildExampleUserRow)
|
||||||
] : null,
|
] : null,
|
||||||
(el) => { setTimeout(() => el.focus(), 0); },
|
(el) => { setTimeout(() => el.focus(), 0); },
|
||||||
|
@ -55,7 +55,7 @@ import {
|
|||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
import isEqual = require('lodash/isEqual');
|
import isEqual = require('lodash/isEqual');
|
||||||
|
|
||||||
const t = makeT('aclui.AccessRules');
|
const t = makeT('AccessRules');
|
||||||
|
|
||||||
// tslint:disable:max-classes-per-file no-console
|
// tslint:disable:max-classes-per-file no-console
|
||||||
|
|
||||||
@ -329,21 +329,21 @@ export class AccessRules extends Disposable {
|
|||||||
bigBasicButton({disabled: true}, dom.hide(this._savingEnabled),
|
bigBasicButton({disabled: true}, dom.hide(this._savingEnabled),
|
||||||
dom.text((use) => {
|
dom.text((use) => {
|
||||||
const s = use(this._ruleStatus);
|
const s = use(this._ruleStatus);
|
||||||
return s === RuleStatus.CheckPending ? t('Checking') :
|
return s === RuleStatus.CheckPending ? t("Checking...") :
|
||||||
s === RuleStatus.Unchanged ? t('Saved') : t('Invalid');
|
s === RuleStatus.Unchanged ? t("Saved") : t("Invalid");
|
||||||
}),
|
}),
|
||||||
testId('rules-non-save')
|
testId('rules-non-save')
|
||||||
),
|
),
|
||||||
bigPrimaryButton(t('Save'), dom.show(this._savingEnabled),
|
bigPrimaryButton(t("Save"), dom.show(this._savingEnabled),
|
||||||
dom.on('click', () => this.save()),
|
dom.on('click', () => this.save()),
|
||||||
testId('rules-save'),
|
testId('rules-save'),
|
||||||
),
|
),
|
||||||
bigBasicButton(t('Reset'), dom.show(use => use(this._ruleStatus) !== RuleStatus.Unchanged),
|
bigBasicButton(t("Reset"), dom.show(use => use(this._ruleStatus) !== RuleStatus.Unchanged),
|
||||||
dom.on('click', () => this.update()),
|
dom.on('click', () => this.update()),
|
||||||
testId('rules-revert'),
|
testId('rules-revert'),
|
||||||
),
|
),
|
||||||
|
|
||||||
bigBasicButton(t('AddTableRules'), cssDropdownIcon('Dropdown'), {style: 'margin-left: auto'},
|
bigBasicButton(t("Add Table Rules"), cssDropdownIcon('Dropdown'), {style: 'margin-left: auto'},
|
||||||
menu(() =>
|
menu(() =>
|
||||||
this.allTableIds.map((tableId) =>
|
this.allTableIds.map((tableId) =>
|
||||||
// Add the table on a timeout, to avoid disabling the clicked menu item
|
// Add the table on a timeout, to avoid disabling the clicked menu item
|
||||||
@ -355,8 +355,8 @@ export class AccessRules extends Disposable {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
bigBasicButton(t('AddUserAttributes'), dom.on('click', () => this._addUserAttributes())),
|
bigBasicButton(t('Add User Attributes'), dom.on('click', () => this._addUserAttributes())),
|
||||||
bigBasicButton(t('ViewAs'), cssDropdownIcon('Dropdown'),
|
bigBasicButton(t('View As'), cssDropdownIcon('Dropdown'),
|
||||||
elem => this._aclUsersPopup.attachPopup(elem, {placement: 'bottom-end'}),
|
elem => this._aclUsersPopup.attachPopup(elem, {placement: 'bottom-end'}),
|
||||||
dom.style('visibility', use => use(this._aclUsersPopup.isInitialized) ? '' : 'hidden')),
|
dom.style('visibility', use => use(this._aclUsersPopup.isInitialized) ? '' : 'hidden')),
|
||||||
),
|
),
|
||||||
@ -375,15 +375,15 @@ export class AccessRules extends Disposable {
|
|||||||
shadowScroll(
|
shadowScroll(
|
||||||
dom.maybe(use => use(this._userAttrRules).length, () =>
|
dom.maybe(use => use(this._userAttrRules).length, () =>
|
||||||
cssSection(
|
cssSection(
|
||||||
cssSectionHeading(t('UserAttributes')),
|
cssSectionHeading(t("User Attributes")),
|
||||||
cssTableRounded(
|
cssTableRounded(
|
||||||
cssTableHeaderRow(
|
cssTableHeaderRow(
|
||||||
cssCell1(cssCell.cls('-rborder'), cssCell.cls('-center'), cssColHeaderCell('Name')),
|
cssCell1(cssCell.cls('-rborder'), cssCell.cls('-center'), cssColHeaderCell('Name')),
|
||||||
cssCell4(
|
cssCell4(
|
||||||
cssColumnGroup(
|
cssColumnGroup(
|
||||||
cssCell1(cssColHeaderCell(t('AttributeToLookUp'))),
|
cssCell1(cssColHeaderCell(t("Attribute to Look Up"))),
|
||||||
cssCell1(cssColHeaderCell(t('LookupTable'))),
|
cssCell1(cssColHeaderCell(t("Lookup Table"))),
|
||||||
cssCell1(cssColHeaderCell(t('LookupColumn'))),
|
cssCell1(cssColHeaderCell(t("Lookup Column"))),
|
||||||
cssCellIcon(),
|
cssCellIcon(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -394,7 +394,7 @@ export class AccessRules extends Disposable {
|
|||||||
),
|
),
|
||||||
dom.forEach(this._tableRules, (tableRules) => tableRules.buildDom()),
|
dom.forEach(this._tableRules, (tableRules) => tableRules.buildDom()),
|
||||||
cssSection(
|
cssSection(
|
||||||
cssSectionHeading(t("DefaultRules"), testId('rule-table-header')),
|
cssSectionHeading(t("Default Rules"), testId('rule-table-header')),
|
||||||
cssTableRounded(
|
cssTableRounded(
|
||||||
cssTableHeaderRow(
|
cssTableHeaderRow(
|
||||||
cssCell1(cssCell.cls('-rborder'), cssCell.cls('-center'), cssColHeaderCell('Columns')),
|
cssCell1(cssCell.cls('-rborder'), cssCell.cls('-center'), cssColHeaderCell('Columns')),
|
||||||
@ -520,7 +520,7 @@ export class AccessRules extends Disposable {
|
|||||||
for (const tableId of tableIds) {
|
for (const tableId of tableIds) {
|
||||||
// We don't know what the table's name was, just its tableId.
|
// We don't know what the table's name was, just its tableId.
|
||||||
// Hopefully, the user will understand.
|
// Hopefully, the user will understand.
|
||||||
const title = t('RemoveRulesMentioningTable', { tableId });
|
const title = t('Remove {{- tableId }} rules', { tableId });
|
||||||
const button = bigBasicButton(title, cssRemoveIcon('Remove'), dom.on('click', async () => {
|
const button = bigBasicButton(title, cssRemoveIcon('Remove'), dom.on('click', async () => {
|
||||||
await Promise.all(this._tableRules.get()
|
await Promise.all(this._tableRules.get()
|
||||||
.filter(rules => rules.tableId === tableId)
|
.filter(rules => rules.tableId === tableId)
|
||||||
@ -546,7 +546,7 @@ export class AccessRules extends Disposable {
|
|||||||
};
|
};
|
||||||
for (const colId of colIds) {
|
for (const colId of colIds) {
|
||||||
// TODO: we could translate tableId to table name in this case.
|
// TODO: we could translate tableId to table name in this case.
|
||||||
const title = t('RemoveRulesMentioningColumn', { tableId, colId });
|
const title = t('Remove column {{- colId }} from {{- tableId }} rules', { tableId, colId });
|
||||||
const button = bigBasicButton(title, cssRemoveIcon('Remove'), dom.on('click', async () => {
|
const button = bigBasicButton(title, cssRemoveIcon('Remove'), dom.on('click', async () => {
|
||||||
await Promise.all(this._tableRules.get()
|
await Promise.all(this._tableRules.get()
|
||||||
.filter(rules => rules.tableId === tableId)
|
.filter(rules => rules.tableId === tableId)
|
||||||
@ -562,7 +562,7 @@ export class AccessRules extends Disposable {
|
|||||||
names: string[]
|
names: string[]
|
||||||
) {
|
) {
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const title = t('RemoveUserAttribute', {name});
|
const title = t('Remove {{- name }} user attribute', {name});
|
||||||
const button = bigBasicButton(title, cssRemoveIcon('Remove'), dom.on('click', async () => {
|
const button = bigBasicButton(title, cssRemoveIcon('Remove'), dom.on('click', async () => {
|
||||||
await Promise.all(this._userAttrRules.get()
|
await Promise.all(this._userAttrRules.get()
|
||||||
.filter(rule => rule.name.get() === name)
|
.filter(rule => rule.name.get() === name)
|
||||||
@ -628,13 +628,13 @@ class TableRules extends Disposable {
|
|||||||
public buildDom() {
|
public buildDom() {
|
||||||
return cssSection(
|
return cssSection(
|
||||||
cssSectionHeading(
|
cssSectionHeading(
|
||||||
dom('span', t('RulesForTable'), cssTableName(this._accessRules.getTableTitle(this.tableId))),
|
dom('span', t("Rules for table "), cssTableName(this._accessRules.getTableTitle(this.tableId))),
|
||||||
cssIconButton(icon('Dots'), {style: 'margin-left: auto'},
|
cssIconButton(icon('Dots'), {style: 'margin-left: auto'},
|
||||||
menu(() => [
|
menu(() => [
|
||||||
menuItemAsync(() => this._addColumnRuleSet(), t('AddColumnRule')),
|
menuItemAsync(() => this._addColumnRuleSet(), t("Add Column Rule")),
|
||||||
menuItemAsync(() => this._addDefaultRuleSet(), t('AddDefaultRule'),
|
menuItemAsync(() => this._addDefaultRuleSet(), t("Add Default Rule"),
|
||||||
dom.cls('disabled', use => Boolean(use(this._defaultRuleSet)))),
|
dom.cls('disabled', use => Boolean(use(this._defaultRuleSet)))),
|
||||||
menuItemAsync(() => this.remove(), t('DeleteTableRules')),
|
menuItemAsync(() => this._accessRules.removeTableRules(this), t("Delete Table Rules")),
|
||||||
]),
|
]),
|
||||||
testId('rule-table-menu-btn'),
|
testId('rule-table-menu-btn'),
|
||||||
),
|
),
|
||||||
@ -762,7 +762,7 @@ class TableRules extends Disposable {
|
|||||||
class SpecialRules extends TableRules {
|
class SpecialRules extends TableRules {
|
||||||
public buildDom() {
|
public buildDom() {
|
||||||
return cssSection(
|
return cssSection(
|
||||||
cssSectionHeading(t('SpecialRules'), testId('rule-table-header')),
|
cssSectionHeading(t("Special Rules"), testId('rule-table-header')),
|
||||||
this.buildColumnRuleSets(),
|
this.buildColumnRuleSets(),
|
||||||
this.buildErrors(),
|
this.buildErrors(),
|
||||||
testId('rule-table'),
|
testId('rule-table'),
|
||||||
@ -1009,17 +1009,18 @@ class DefaultObsRuleSet extends ObsRuleSet {
|
|||||||
function getSpecialRuleDescription(type: string): string {
|
function getSpecialRuleDescription(type: string): string {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'AccessRules':
|
case 'AccessRules':
|
||||||
return t('AccessRulesDescription');
|
return t("Allow everyone to view Access Rules.");
|
||||||
case 'FullCopies':
|
case 'FullCopies':
|
||||||
return t('FullCopiesDescription');
|
return t(`Allow everyone to copy the entire document, or view it in full in fiddle mode.
|
||||||
|
Useful for examples and templates, but not for sensitive data.`);
|
||||||
default: return type;
|
default: return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSpecialRuleName(type: string): string {
|
function getSpecialRuleName(type: string): string {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'AccessRules': return t('AccessRulesName');
|
case 'AccessRules': return t("Permission to view Access Rules");
|
||||||
case 'FullCopies': return t('FullCopies');
|
case 'FullCopies': return t("Permission to access the document in full when needed");
|
||||||
default: return type;
|
default: return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1157,7 +1158,7 @@ class ObsUserAttributeRule extends Disposable {
|
|||||||
cssCell1(cssCell.cls('-rborder'),
|
cssCell1(cssCell.cls('-rborder'),
|
||||||
cssCellContent(
|
cssCellContent(
|
||||||
cssInput(this._name, async (val) => this._name.set(val),
|
cssInput(this._name, async (val) => this._name.set(val),
|
||||||
{placeholder: t('AttributeNamePlaceholder')},
|
{placeholder: t("Attribute name")},
|
||||||
(this._options.focus ? (elem) => { setTimeout(() => elem.focus(), 0); } : null),
|
(this._options.focus ? (elem) => { setTimeout(() => elem.focus(), 0); } : null),
|
||||||
testId('rule-userattr-name'),
|
testId('rule-userattr-name'),
|
||||||
),
|
),
|
||||||
@ -1395,7 +1396,7 @@ class ObsRulePart extends Disposable {
|
|||||||
placeholder: dom.text((use) => {
|
placeholder: dom.text((use) => {
|
||||||
return (
|
return (
|
||||||
this._ruleSet.isSoleCondition(use, this) ? t('Everyone') :
|
this._ruleSet.isSoleCondition(use, this) ? t('Everyone') :
|
||||||
this._ruleSet.isLastCondition(use, this) ? t('EveryoneElse') :
|
this._ruleSet.isLastCondition(use, this) ? t('Everyone Else') :
|
||||||
t('EnterCondition')
|
t('EnterCondition')
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -1444,7 +1445,7 @@ class ObsRulePart extends Disposable {
|
|||||||
wide ? cssCell4.cls('') : null,
|
wide ? cssCell4.cls('') : null,
|
||||||
this._memoEditor = aclMemoEditor(this._memo,
|
this._memoEditor = aclMemoEditor(this._memo,
|
||||||
{
|
{
|
||||||
placeholder: t('MemoEditorPlaceholder'),
|
placeholder: t("Type a message..."),
|
||||||
},
|
},
|
||||||
dom.onKeyDown({
|
dom.onKeyDown({
|
||||||
// Match the behavior of the formula editor.
|
// Match the behavior of the formula editor.
|
||||||
|
@ -15,7 +15,7 @@ import {makeT} from 'app/client/lib/localization';
|
|||||||
// One of the strings 'read', 'update', etc.
|
// One of the strings 'read', 'update', etc.
|
||||||
export type PermissionKey = keyof PartialPermissionSet;
|
export type PermissionKey = keyof PartialPermissionSet;
|
||||||
|
|
||||||
const t = makeT('aclui.PermissionsWidget');
|
const t = makeT('PermissionsWidget');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a box for each of availableBits, and a dropdown with a description and some shortcuts.
|
* Renders a box for each of availableBits, and a dropdown with a description and some shortcuts.
|
||||||
@ -64,13 +64,13 @@ export function permissionsWidget(
|
|||||||
null
|
null
|
||||||
),
|
),
|
||||||
// If the set matches any recognized pattern, mark that item with a tick (checkmark).
|
// If the set matches any recognized pattern, mark that item with a tick (checkmark).
|
||||||
cssMenuItem(() => setPermissions(allowAll), tick(isEqual(pset.get(), allowAll)), t('AllowAll'),
|
cssMenuItem(() => setPermissions(allowAll), tick(isEqual(pset.get(), allowAll)), t("Allow All"),
|
||||||
dom.cls('disabled', options.disabled)
|
dom.cls('disabled', options.disabled)
|
||||||
),
|
),
|
||||||
cssMenuItem(() => setPermissions(denyAll), tick(isEqual(pset.get(), denyAll)), t('DenyAll'),
|
cssMenuItem(() => setPermissions(denyAll), tick(isEqual(pset.get(), denyAll)), t("Deny All"),
|
||||||
dom.cls('disabled', options.disabled)
|
dom.cls('disabled', options.disabled)
|
||||||
),
|
),
|
||||||
cssMenuItem(() => setPermissions(readOnly), tick(isEqual(pset.get(), readOnly)), t('ReadOnly'),
|
cssMenuItem(() => setPermissions(readOnly), tick(isEqual(pset.get(), readOnly)), t("Read Only"),
|
||||||
dom.cls('disabled', options.disabled)
|
dom.cls('disabled', options.disabled)
|
||||||
),
|
),
|
||||||
cssMenuItem(() => setPermissions(empty),
|
cssMenuItem(() => setPermissions(empty),
|
||||||
|
@ -47,7 +47,7 @@ const state = {
|
|||||||
DEFAULT: 'default'
|
DEFAULT: 'default'
|
||||||
};
|
};
|
||||||
|
|
||||||
const t = makeT('components.ActionLog');
|
const t = makeT('ActionLog');
|
||||||
|
|
||||||
export class ActionLog extends dispose.Disposable implements IDomComponent {
|
export class ActionLog extends dispose.Disposable implements IDomComponent {
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _buildLogDom() {
|
private _buildLogDom() {
|
||||||
this._loadActionSummaries().catch((error) => gristNotify(t("ActionLogFailed")));
|
this._loadActionSummaries().catch((error) => gristNotify(t("Action Log failed to load")));
|
||||||
return dom('div.action_log',
|
return dom('div.action_log',
|
||||||
dom('div.preference_item',
|
dom('div.preference_item',
|
||||||
koForm.checkbox(this._showAllTables,
|
koForm.checkbox(this._showAllTables,
|
||||||
@ -395,7 +395,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
|||||||
const newName = tableRename[1];
|
const newName = tableRename[1];
|
||||||
if (!newName) {
|
if (!newName) {
|
||||||
// TODO - find a better way to send informative notifications.
|
// TODO - find a better way to send informative notifications.
|
||||||
gristNotify(t('TableRemovedInAction', {tableId:tableId, actionNum: action.actionNum}));
|
gristNotify(t("Table {{tableId}} was subsequently removed in action #{{actionNum}}", {tableId:tableId, actionNum: action.actionNum}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tableId = newName;
|
tableId = newName;
|
||||||
@ -406,7 +406,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
|||||||
// Check is this row was removed - if so there's no reason to go on.
|
// Check is this row was removed - if so there's no reason to go on.
|
||||||
if (td.removeRows.indexOf(rowId) >= 0) {
|
if (td.removeRows.indexOf(rowId) >= 0) {
|
||||||
// TODO - find a better way to send informative notifications.
|
// TODO - find a better way to send informative notifications.
|
||||||
gristNotify(t("RowRemovedInAction", {actionNum}));
|
gristNotify(t("This row was subsequently removed in action {{action.actionNum}}", {actionNum}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
|||||||
const newName = columnRename[1];
|
const newName = columnRename[1];
|
||||||
if (!newName) {
|
if (!newName) {
|
||||||
// TODO - find a better way to send informative notifications.
|
// TODO - find a better way to send informative notifications.
|
||||||
gristNotify(t("ColumnRemovedInAction", {colId, actionNum: action.actionNum}));
|
gristNotify(t("Column {{colId}} was subsequently removed in action #{{action.actionNum}}", {colId, actionNum: action.actionNum}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
colId = newName;
|
colId = newName;
|
||||||
|
@ -50,7 +50,7 @@ const DONUT_DEFAULT_TEXT_SIZE = 24;
|
|||||||
|
|
||||||
const testId = makeTestId('test-chart-');
|
const testId = makeTestId('test-chart-');
|
||||||
|
|
||||||
const t = makeT('components.ChartView');
|
const t = makeT('ChartView');
|
||||||
|
|
||||||
function isPieLike(chartType: string) {
|
function isPieLike(chartType: string) {
|
||||||
return ['pie', 'donut'].includes(chartType);
|
return ['pie', 'donut'].includes(chartType);
|
||||||
@ -655,8 +655,8 @@ export class ChartConfig extends GrainJSDisposable {
|
|||||||
testId('error-bars'),
|
testId('error-bars'),
|
||||||
),
|
),
|
||||||
dom.domComputed(this._optionsObj.prop('errorBars'), (value: ChartOptions["errorBars"]) =>
|
dom.domComputed(this._optionsObj.prop('errorBars'), (value: ChartOptions["errorBars"]) =>
|
||||||
value === 'symmetric' ? cssRowHelp(t('EachYFollowedByOne')) :
|
value === 'symmetric' ? cssRowHelp(t("Each Y series is followed by a series for the length of error bars.")) :
|
||||||
value === 'separate' ? cssRowHelp(t('EachYFollowedByTwo')) :
|
value === 'separate' ? cssRowHelp(t("Each Y series is followed by two series, for top and bottom error bars.")) :
|
||||||
null
|
null
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@ -669,7 +669,7 @@ export class ChartConfig extends GrainJSDisposable {
|
|||||||
select(this._groupDataColId, this._groupDataOptions),
|
select(this._groupDataColId, this._groupDataOptions),
|
||||||
testId('group-by-column'),
|
testId('group-by-column'),
|
||||||
),
|
),
|
||||||
cssHintRow(t('CreateSeparateSeries')),
|
cssHintRow(t("Create separate series for each value of the selected column.")),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
// TODO: user should select x axis before widget reach page
|
// TODO: user should select x axis before widget reach page
|
||||||
@ -677,7 +677,7 @@ export class ChartConfig extends GrainJSDisposable {
|
|||||||
cssRow(
|
cssRow(
|
||||||
select(
|
select(
|
||||||
this._xAxis, this._columnsOptions,
|
this._xAxis, this._columnsOptions,
|
||||||
{ defaultLabel: t('PickColumn') }
|
{ defaultLabel: t("Pick a column") }
|
||||||
),
|
),
|
||||||
testId('x-axis'),
|
testId('x-axis'),
|
||||||
),
|
),
|
||||||
@ -773,7 +773,7 @@ export class ChartConfig extends GrainJSDisposable {
|
|||||||
private async _setGroupDataColumn(colId: string) {
|
private async _setGroupDataColumn(colId: string) {
|
||||||
const viewFields = this._section.viewFields.peek().peek();
|
const viewFields = this._section.viewFields.peek().peek();
|
||||||
|
|
||||||
await this._gristDoc.docData.bundleActions(t('SelectedNewGroupDataColumns'), async () => {
|
await this._gristDoc.docData.bundleActions(t("selected new group data columns"), async () => {
|
||||||
this._freezeXAxis.set(true);
|
this._freezeXAxis.set(true);
|
||||||
this._freezeYAxis.set(true);
|
this._freezeYAxis.set(true);
|
||||||
try {
|
try {
|
||||||
@ -872,7 +872,7 @@ export class ChartConfig extends GrainJSDisposable {
|
|||||||
private async _setAggregation(val: boolean) {
|
private async _setAggregation(val: boolean) {
|
||||||
try {
|
try {
|
||||||
this._freezeXAxis.set(true);
|
this._freezeXAxis.set(true);
|
||||||
await this._gristDoc.docData.bundleActions(t("ToggleChartAggregation"), async () => {
|
await this._gristDoc.docData.bundleActions(t("Toggle chart aggregation"), async () => {
|
||||||
if (val) {
|
if (val) {
|
||||||
await this._doAggregation();
|
await this._doAggregation();
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,7 +9,7 @@ import {makeT} from 'app/client/lib/localization';
|
|||||||
const hljs = require('highlight.js/lib/core');
|
const hljs = require('highlight.js/lib/core');
|
||||||
hljs.registerLanguage('python', require('highlight.js/lib/languages/python'));
|
hljs.registerLanguage('python', require('highlight.js/lib/languages/python'));
|
||||||
|
|
||||||
const t = makeT('components.CodeEditorPanel');
|
const t = makeT('CodeEditorPanel');
|
||||||
|
|
||||||
export class CodeEditorPanel extends DisposableWithEvents {
|
export class CodeEditorPanel extends DisposableWithEvents {
|
||||||
private _schema = Observable.create(this, '');
|
private _schema = Observable.create(this, '');
|
||||||
@ -28,8 +28,8 @@ export class CodeEditorPanel extends DisposableWithEvents {
|
|||||||
return dom('div.g-code-panel.clipboard',
|
return dom('div.g-code-panel.clipboard',
|
||||||
{tabIndex: "-1"},
|
{tabIndex: "-1"},
|
||||||
dom.maybe(this._denied, () => dom('div.g-code-panel-denied',
|
dom.maybe(this._denied, () => dom('div.g-code-panel-denied',
|
||||||
dom('h2', dom.text(t('AccessDenied'))),
|
dom('h2', dom.text(t("Access denied"))),
|
||||||
dom('div', dom.text(t('CodeViewOnlyFullAccess'))),
|
dom('div', dom.text(t("Code View is available only when you have full document access."))),
|
||||||
)),
|
)),
|
||||||
dom.maybe(this._schema, (schema) => {
|
dom.maybe(this._schema, (schema) => {
|
||||||
// The reason to scope and rebuild instead of using `kd.text(schema)` is because
|
// The reason to scope and rebuild instead of using `kd.text(schema)` is because
|
||||||
|
@ -16,7 +16,7 @@ import {makeT} from 'app/client/lib/localization';
|
|||||||
|
|
||||||
const testId = makeTestId('test-raw-data-');
|
const testId = makeTestId('test-raw-data-');
|
||||||
|
|
||||||
const t = makeT('components.DataTables');
|
const t = makeT('DataTables');
|
||||||
|
|
||||||
export class DataTables extends Disposable {
|
export class DataTables extends Disposable {
|
||||||
private _tables: Observable<TableRec[]>;
|
private _tables: Observable<TableRec[]>;
|
||||||
@ -45,7 +45,7 @@ export class DataTables extends Disposable {
|
|||||||
cssTableList(
|
cssTableList(
|
||||||
/*************** List section **********/
|
/*************** List section **********/
|
||||||
testId('list'),
|
testId('list'),
|
||||||
cssHeader(t('RawDataTables')),
|
cssHeader(t("Raw Data Tables")),
|
||||||
cssList(
|
cssList(
|
||||||
dom.forEach(this._tables, tableRec =>
|
dom.forEach(this._tables, tableRec =>
|
||||||
cssItem(
|
cssItem(
|
||||||
@ -65,11 +65,11 @@ export class DataTables extends Disposable {
|
|||||||
testId('table-id'),
|
testId('table-id'),
|
||||||
dom.text(tableRec.tableId),
|
dom.text(tableRec.tableId),
|
||||||
),
|
),
|
||||||
{ title : t('ClickToCopy') },
|
{ title : t("Click to copy") },
|
||||||
dom.on('click', async (e, d) => {
|
dom.on('click', async (e, d) => {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
showTransientTooltip(d, t('TableIDCopied'), {
|
showTransientTooltip(d, t("Table ID copied to clipboard"), {
|
||||||
key: 'copy-table-id'
|
key: 'copy-table-id'
|
||||||
});
|
});
|
||||||
await copyToClipboard(tableRec.tableId.peek());
|
await copyToClipboard(tableRec.tableId.peek());
|
||||||
@ -127,7 +127,7 @@ export class DataTables extends Disposable {
|
|||||||
return [
|
return [
|
||||||
menuItem(
|
menuItem(
|
||||||
() => this._duplicateTable(table),
|
() => this._duplicateTable(table),
|
||||||
t('DuplicateTable'),
|
t("Duplicate Table"),
|
||||||
testId('menu-duplicate-table'),
|
testId('menu-duplicate-table'),
|
||||||
dom.cls('disabled', use =>
|
dom.cls('disabled', use =>
|
||||||
use(isReadonly) ||
|
use(isReadonly) ||
|
||||||
@ -144,7 +144,7 @@ export class DataTables extends Disposable {
|
|||||||
use(docModel.visibleTables.getObservable()).length <= 1 && !use(table.isHidden)
|
use(docModel.visibleTables.getObservable()).length <= 1 && !use(table.isHidden)
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
dom.maybe(isReadonly, () => menuText(t("NoEditAccess"))),
|
dom.maybe(isReadonly, () => menuText(t("You do not have edit access to this document"))),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ export class DataTables extends Disposable {
|
|||||||
function doRemove() {
|
function doRemove() {
|
||||||
return docModel.docData.sendAction(['RemoveTable', r.tableId()]);
|
return docModel.docData.sendAction(['RemoveTable', r.tableId()]);
|
||||||
}
|
}
|
||||||
confirmModal(t("DeleteData", {formattedTableName : r.formattedTableName()}), 'Delete', doRemove);
|
confirmModal(t("Delete {{formattedTableName}} data, and remove it from all pages?", {formattedTableName : r.formattedTableName()}), 'Delete', doRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _tableRows(table: TableRec) {
|
private _tableRows(table: TableRec) {
|
||||||
|
@ -13,7 +13,7 @@ import {canUpgradeOrg} from 'app/common/roles';
|
|||||||
import {Computed, Disposable, dom, DomContents, DomElementArg, makeTestId, styled} from 'grainjs';
|
import {Computed, Disposable, dom, DomContents, DomElementArg, makeTestId, styled} from 'grainjs';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('components.DocumentUsage');
|
const t = makeT('DocumentUsage');
|
||||||
|
|
||||||
const testId = makeTestId('test-doc-usage-');
|
const testId = makeTestId('test-doc-usage-');
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ export class DocumentUsage extends Disposable {
|
|||||||
// Invalid row limits are currently treated as if they are undefined.
|
// Invalid row limits are currently treated as if they are undefined.
|
||||||
const maxValue = maxRows && maxRows > 0 ? maxRows : undefined;
|
const maxValue = maxRows && maxRows > 0 ? maxRows : undefined;
|
||||||
return {
|
return {
|
||||||
name: t('Rows'),
|
name: t("Rows"),
|
||||||
currentValue: typeof rowCount !== 'object' ? undefined : rowCount.total,
|
currentValue: typeof rowCount !== 'object' ? undefined : rowCount.total,
|
||||||
maximumValue: maxValue ?? DEFAULT_MAX_ROWS,
|
maximumValue: maxValue ?? DEFAULT_MAX_ROWS,
|
||||||
unit: 'rows',
|
unit: 'rows',
|
||||||
@ -75,7 +75,7 @@ export class DocumentUsage extends Disposable {
|
|||||||
// Invalid data size limits are currently treated as if they are undefined.
|
// Invalid data size limits are currently treated as if they are undefined.
|
||||||
const maxValue = maxSize && maxSize > 0 ? maxSize : undefined;
|
const maxValue = maxSize && maxSize > 0 ? maxSize : undefined;
|
||||||
return {
|
return {
|
||||||
name: t('DataSize'),
|
name: t("Data Size"),
|
||||||
currentValue: typeof dataSize !== 'number' ? undefined : dataSize,
|
currentValue: typeof dataSize !== 'number' ? undefined : dataSize,
|
||||||
maximumValue: maxValue ?? DEFAULT_MAX_DATA_SIZE,
|
maximumValue: maxValue ?? DEFAULT_MAX_DATA_SIZE,
|
||||||
unit: 'MB',
|
unit: 'MB',
|
||||||
@ -97,7 +97,7 @@ export class DocumentUsage extends Disposable {
|
|||||||
// Invalid attachments size limits are currently treated as if they are undefined.
|
// Invalid attachments size limits are currently treated as if they are undefined.
|
||||||
const maxValue = maxSize && maxSize > 0 ? maxSize : undefined;
|
const maxValue = maxSize && maxSize > 0 ? maxSize : undefined;
|
||||||
return {
|
return {
|
||||||
name: t('AttachmentsSize'),
|
name: t("Attachments Size"),
|
||||||
currentValue: typeof attachmentsSize !== 'number' ? undefined : attachmentsSize,
|
currentValue: typeof attachmentsSize !== 'number' ? undefined : attachmentsSize,
|
||||||
maximumValue: maxValue ?? DEFAULT_MAX_ATTACHMENTS_SIZE,
|
maximumValue: maxValue ?? DEFAULT_MAX_ATTACHMENTS_SIZE,
|
||||||
unit: 'GB',
|
unit: 'GB',
|
||||||
@ -135,7 +135,7 @@ export class DocumentUsage extends Disposable {
|
|||||||
|
|
||||||
public buildDom() {
|
public buildDom() {
|
||||||
return dom('div',
|
return dom('div',
|
||||||
cssHeader(t('Usage'), testId('heading')),
|
cssHeader(t("Usage"), testId('heading')),
|
||||||
dom.domComputed(this._areAllMetricsPending, (isLoading) => {
|
dom.domComputed(this._areAllMetricsPending, (isLoading) => {
|
||||||
if (isLoading) { return cssSpinner(loadingSpinner(), testId('loading')); }
|
if (isLoading) { return cssSpinner(loadingSpinner(), testId('loading')); }
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ export class DocumentUsage extends Disposable {
|
|||||||
return dom.domComputed((use) => {
|
return dom.domComputed((use) => {
|
||||||
const isAccessDenied = use(this._isAccessDenied);
|
const isAccessDenied = use(this._isAccessDenied);
|
||||||
if (isAccessDenied === null) { return null; }
|
if (isAccessDenied === null) { return null; }
|
||||||
if (isAccessDenied) { return buildMessage(t('UsageStatisticsOnlyFullAccess')); }
|
if (isAccessDenied) { return buildMessage(t("Usage statistics are only available to users with full access to the document data.")); }
|
||||||
|
|
||||||
const org = use(this._currentOrg);
|
const org = use(this._currentOrg);
|
||||||
const product = use(this._currentProduct);
|
const product = use(this._currentProduct);
|
||||||
@ -237,12 +237,12 @@ export function buildUpgradeMessage(
|
|||||||
variant: 'short' | 'long',
|
variant: 'short' | 'long',
|
||||||
onUpgrade: () => void,
|
onUpgrade: () => void,
|
||||||
) {
|
) {
|
||||||
if (!canUpgrade) { return t('LimitContactSiteOwner'); }
|
if (!canUpgrade) { return t("Contact the site owner to upgrade the plan to raise limits."); }
|
||||||
|
|
||||||
const upgradeLinkText = t('UpgradeLinkText')
|
const upgradeLinkText = t("start your 30-day free trial of the Pro plan.")
|
||||||
// TODO i18next
|
// TODO i18next
|
||||||
return [
|
return [
|
||||||
variant === 'short' ? null : t('ForHigherLimits'),
|
variant === 'short' ? null : t("For higher limits, "),
|
||||||
buildUpgradeLink(
|
buildUpgradeLink(
|
||||||
variant === 'short' ? capitalizeFirstWord(upgradeLinkText) : upgradeLinkText,
|
variant === 'short' ? capitalizeFirstWord(upgradeLinkText) : upgradeLinkText,
|
||||||
() => onUpgrade(),
|
() => onUpgrade(),
|
||||||
|
@ -273,7 +273,7 @@ class NotificationAdapter extends Disposable implements Notification {
|
|||||||
}
|
}
|
||||||
public showUndoDiscard() {
|
public showUndoDiscard() {
|
||||||
const notifier = this._doc.app.topAppModel.notifier;
|
const notifier = this._doc.app.topAppModel.notifier;
|
||||||
const notification = notifier.createUserMessage(t("UndoDiscard"), {
|
const notification = notifier.createUserMessage(t("Undo discard"), {
|
||||||
message: () =>
|
message: () =>
|
||||||
discardNotification(
|
discardNotification(
|
||||||
dom.on("click", () => {
|
dom.on("click", () => {
|
||||||
@ -421,7 +421,7 @@ const styledTooltip = styled('div', `
|
|||||||
function cellTooltip(clb: () => any) {
|
function cellTooltip(clb: () => any) {
|
||||||
return function (ctl: ITooltipControl) {
|
return function (ctl: ITooltipControl) {
|
||||||
return styledTooltip(
|
return styledTooltip(
|
||||||
cssLink(t('RestoreLastEdit'),
|
cssLink(t("Restore last edit"),
|
||||||
dom.on('mousedown', (ev) => { ev.preventDefault(); ctl.close(); clb(); }),
|
dom.on('mousedown', (ev) => { ev.preventDefault(); ctl.close(); clb(); }),
|
||||||
testId('draft-tooltip'),
|
testId('draft-tooltip'),
|
||||||
),
|
),
|
||||||
@ -440,7 +440,7 @@ const styledNotification = styled('div', `
|
|||||||
`);
|
`);
|
||||||
function discardNotification(...args: IDomArgs<TagElem<"div">>) {
|
function discardNotification(...args: IDomArgs<TagElem<"div">>) {
|
||||||
return styledNotification(
|
return styledNotification(
|
||||||
t("UndoDiscard"),
|
t("Undo discard"),
|
||||||
testId("draft-notification"),
|
testId("draft-notification"),
|
||||||
...args
|
...args
|
||||||
);
|
);
|
||||||
|
@ -87,7 +87,7 @@ import * as ko from 'knockout';
|
|||||||
import cloneDeepWith = require('lodash/cloneDeepWith');
|
import cloneDeepWith = require('lodash/cloneDeepWith');
|
||||||
import isEqual = require('lodash/isEqual');
|
import isEqual = require('lodash/isEqual');
|
||||||
|
|
||||||
const t = makeT('components.GristDoc');
|
const t = makeT('GristDoc');
|
||||||
|
|
||||||
const G = getBrowserGlobals('document', 'window');
|
const G = getBrowserGlobals('document', 'window');
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
const importSourceElems = ImportSourceElement.fromArray(this.docPluginManager.pluginsList);
|
const importSourceElems = ImportSourceElement.fromArray(this.docPluginManager.pluginsList);
|
||||||
const importMenuItems = [
|
const importMenuItems = [
|
||||||
{
|
{
|
||||||
label: t('ImportFromFile'),
|
label: t("Import from file"),
|
||||||
action: () => Importer.selectAndImport(this, importSourceElems, null, createPreview),
|
action: () => Importer.selectAndImport(this, importSourceElems, null, createPreview),
|
||||||
},
|
},
|
||||||
...importSourceElems.map(importSourceElem => ({
|
...importSourceElems.map(importSourceElem => ({
|
||||||
@ -599,7 +599,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const res = await docData.bundleActions(
|
const res = await docData.bundleActions(
|
||||||
t("AddedNewLinkedSection", {viewName}),
|
t("Added new linked section to view {{viewName}}", {viewName}),
|
||||||
() => this.addWidgetToPageImpl(val, tableId ?? null)
|
() => this.addWidgetToPageImpl(val, tableId ?? null)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -680,7 +680,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return await this._viewLayout!.freezeUntil(docData.bundleActions(
|
return await this._viewLayout!.freezeUntil(docData.bundleActions(
|
||||||
t("SavedLinkedSectionIn", {title:section.title(), name: viewModel.name()}),
|
t("Saved linked section {{title}} in view {{name}}", {title:section.title(), name: viewModel.name()}),
|
||||||
async () => {
|
async () => {
|
||||||
|
|
||||||
// if table changes or a table is made a summary table, let's replace the view section by a
|
// if table changes or a table is made a summary table, let's replace the view section by a
|
||||||
|
@ -36,7 +36,7 @@ import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox';
|
|||||||
import {ACCESS_DENIED, AUTH_INTERRUPTED, canReadPrivateFiles, getGoogleCodeForReading} from 'app/client/ui/googleAuth';
|
import {ACCESS_DENIED, AUTH_INTERRUPTED, canReadPrivateFiles, getGoogleCodeForReading} from 'app/client/ui/googleAuth';
|
||||||
import debounce = require('lodash/debounce');
|
import debounce = require('lodash/debounce');
|
||||||
|
|
||||||
const t = makeT('components.Importer');
|
const t = makeT('Importer');
|
||||||
|
|
||||||
|
|
||||||
// We expect a function for creating the preview GridView, to avoid the need to require the
|
// We expect a function for creating the preview GridView, to avoid the need to require the
|
||||||
@ -631,7 +631,7 @@ export class Importer extends DisposableWithEvents {
|
|||||||
cssMergeOptions(
|
cssMergeOptions(
|
||||||
cssMergeOptionsToggle(labeledSquareCheckbox(
|
cssMergeOptionsToggle(labeledSquareCheckbox(
|
||||||
updateExistingRecords,
|
updateExistingRecords,
|
||||||
t('UpdateExistingRecords'),
|
t("Update existing records"),
|
||||||
dom.autoDispose(updateRecordsListener),
|
dom.autoDispose(updateRecordsListener),
|
||||||
testId('importer-update-existing-records')
|
testId('importer-update-existing-records')
|
||||||
)),
|
)),
|
||||||
@ -646,14 +646,14 @@ export class Importer extends DisposableWithEvents {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
cssMergeOptionsMessage(
|
cssMergeOptionsMessage(
|
||||||
t('MergeRowsThatMatch'),
|
t("Merge rows that match these fields:"),
|
||||||
testId('importer-merge-fields-message')
|
testId('importer-merge-fields-message')
|
||||||
),
|
),
|
||||||
multiSelect(
|
multiSelect(
|
||||||
mergeCols,
|
mergeCols,
|
||||||
section.viewFields().peek().map(f => ({label: f.label(), value: f.colId()})) ?? [],
|
section.viewFields().peek().map(f => ({label: f.label(), value: f.colId()})) ?? [],
|
||||||
{
|
{
|
||||||
placeholder: t("SelectFieldsToMatch"),
|
placeholder: t("Select fields to match on"),
|
||||||
error: hasInvalidMergeCols
|
error: hasInvalidMergeCols
|
||||||
},
|
},
|
||||||
dom.autoDispose(mergeColsListener),
|
dom.autoDispose(mergeColsListener),
|
||||||
|
@ -7,7 +7,7 @@ import { PluginInstance } from 'app/common/PluginInstance';
|
|||||||
import { RenderTarget } from 'app/plugin/RenderOptions';
|
import { RenderTarget } from 'app/plugin/RenderOptions';
|
||||||
import { Disposable, dom, DomContents, Observable, styled } from 'grainjs';
|
import { Disposable, dom, DomContents, Observable, styled } from 'grainjs';
|
||||||
|
|
||||||
const t = makeT('components.PluginScreen');
|
const t = makeT('PluginScreen');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rendering options for the PluginScreen modal.
|
* Rendering options for the PluginScreen modal.
|
||||||
@ -55,7 +55,7 @@ export class PluginScreen extends Disposable {
|
|||||||
public renderError(message: string) {
|
public renderError(message: string) {
|
||||||
this.render([
|
this.render([
|
||||||
this._buildModalTitle(),
|
this._buildModalTitle(),
|
||||||
cssModalBody(t('ImportFailed'), message, testId('importer-error')),
|
cssModalBody(t("Import failed: "), message, testId('importer-error')),
|
||||||
cssModalButtons(
|
cssModalButtons(
|
||||||
bigBasicButton('Close',
|
bigBasicButton('Close',
|
||||||
dom.on('click', () => this.close()),
|
dom.on('click', () => this.close()),
|
||||||
|
@ -41,7 +41,7 @@ var {menu} = require('../ui2018/menus');
|
|||||||
var {testId} = require('app/client/ui2018/cssVars');
|
var {testId} = require('app/client/ui2018/cssVars');
|
||||||
var {contextMenu} = require('app/client/ui/contextMenu');
|
var {contextMenu} = require('app/client/ui/contextMenu');
|
||||||
|
|
||||||
const t = makeT('components.RecordLayout');
|
const t = makeT('RecordLayout');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a RecordLayout.
|
* Construct a RecordLayout.
|
||||||
@ -263,7 +263,7 @@ RecordLayout.prototype.saveLayoutSpec = async function(layoutSpec) {
|
|||||||
// Use separate copies of addColAction, since sendTableActions modified each in-place.
|
// Use separate copies of addColAction, since sendTableActions modified each in-place.
|
||||||
let addActions = gutil.arrayRepeat(addColNum, 0).map(() => addColAction.slice());
|
let addActions = gutil.arrayRepeat(addColNum, 0).map(() => addColAction.slice());
|
||||||
|
|
||||||
await docData.bundleActions(t('UpdatingRecordLayout'), () => {
|
await docData.bundleActions(t("Updating record layout."), () => {
|
||||||
return Promise.try(() => {
|
return Promise.try(() => {
|
||||||
return addColNum > 0 ? docModel.dataTables[tableId].sendTableActions(addActions) : [];
|
return addColNum > 0 ? docModel.dataTables[tableId].sendTableActions(addActions) : [];
|
||||||
})
|
})
|
||||||
|
@ -6,7 +6,7 @@ var {makeT} = require('app/client/lib/localization');
|
|||||||
var commands = require('./commands');
|
var commands = require('./commands');
|
||||||
var LayoutEditor = require('./LayoutEditor');
|
var LayoutEditor = require('./LayoutEditor');
|
||||||
|
|
||||||
const t = makeT('components.RecordLayoutEditor');
|
const t = makeT('RecordLayoutEditor');
|
||||||
const {basicButton, cssButton, primaryButton} = require('app/client/ui2018/buttons');
|
const {basicButton, cssButton, primaryButton} = require('app/client/ui2018/buttons');
|
||||||
const {icon} = require('app/client/ui2018/icons');
|
const {icon} = require('app/client/ui2018/icons');
|
||||||
const {menu, menuDivider, menuItem} = require('app/client/ui2018/menus');
|
const {menu, menuDivider, menuItem} = require('app/client/ui2018/menus');
|
||||||
@ -93,13 +93,13 @@ RecordLayoutEditor.prototype.buildEditorDom = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return cssControls(
|
return cssControls(
|
||||||
basicButton(t('AddField'), cssCollapseIcon('Collapse'),
|
basicButton(t('Add Field'), cssCollapseIcon('Collapse'),
|
||||||
menu((ctl) => [
|
menu((ctl) => [
|
||||||
menuItem(() => addNewField(), t('CreateNewField')),
|
menuItem(() => addNewField(), t('Create New Field')),
|
||||||
dom.maybe((use) => use(this._hiddenColumns).length > 0,
|
dom.maybe((use) => use(this._hiddenColumns).length > 0,
|
||||||
() => menuDivider()),
|
() => menuDivider()),
|
||||||
dom.forEach(this._hiddenColumns, (col) =>
|
dom.forEach(this._hiddenColumns, (col) =>
|
||||||
menuItem(() => showField(col), t("ShowField", {label:col.label()}))
|
menuItem(() => showField(col), t("Show field {{- label}}", {label:col.label()}))
|
||||||
),
|
),
|
||||||
testId('edit-layout-add-menu'),
|
testId('edit-layout-add-menu'),
|
||||||
]),
|
]),
|
||||||
@ -113,7 +113,7 @@ RecordLayoutEditor.prototype.buildEditorDom = function() {
|
|||||||
|
|
||||||
RecordLayoutEditor.prototype.buildFinishButtons = function() {
|
RecordLayoutEditor.prototype.buildFinishButtons = function() {
|
||||||
return [
|
return [
|
||||||
primaryButton(t('SaveLayout'),
|
primaryButton(t('Save Layout'),
|
||||||
dom.on('click', () => commands.allCommands.accept.run()),
|
dom.on('click', () => commands.allCommands.accept.run()),
|
||||||
),
|
),
|
||||||
basicButton(t('Cancel'),
|
basicButton(t('Cancel'),
|
||||||
|
@ -14,7 +14,7 @@ import ko from 'knockout';
|
|||||||
import {menu, menuItem} from 'popweasel';
|
import {menu, menuItem} from 'popweasel';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('components.RefSelect');
|
const t = makeT('RefSelect');
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
label: string;
|
label: string;
|
||||||
@ -97,7 +97,7 @@ export class RefSelect extends Disposable {
|
|||||||
testId('ref-select-item'),
|
testId('ref-select-item'),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
cssAddLink(cssAddIcon('Plus'), t('AddColumn'),
|
cssAddLink(cssAddIcon('Plus'), t("Add Column"),
|
||||||
menu(() => [
|
menu(() => [
|
||||||
...this._validCols.peek()
|
...this._validCols.peek()
|
||||||
.filter((col) => !this._addedSet.peek().has(col.colId.peek()))
|
.filter((col) => !this._addedSet.peek().has(col.colId.peek()))
|
||||||
@ -105,7 +105,7 @@ export class RefSelect extends Disposable {
|
|||||||
menuItem(() => this._addFormulaField({ label: col.label(), value: col.colId() }),
|
menuItem(() => this._addFormulaField({ label: col.label(), value: col.colId() }),
|
||||||
col.label.peek())
|
col.label.peek())
|
||||||
),
|
),
|
||||||
cssEmptyMenuText(t("NoColumnsAdd")),
|
cssEmptyMenuText(t("No columns to add")),
|
||||||
testId('ref-select-menu'),
|
testId('ref-select-menu'),
|
||||||
]),
|
]),
|
||||||
testId('ref-select-add'),
|
testId('ref-select-add'),
|
||||||
|
@ -16,7 +16,7 @@ import ko from 'knockout';
|
|||||||
import {Computed, Disposable, dom, makeTestId, Observable, styled, subscribe} from 'grainjs';
|
import {Computed, Disposable, dom, makeTestId, Observable, styled, subscribe} from 'grainjs';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('components.SelectionSummary');
|
const t = makeT('SelectionSummary');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A beginning and end index for a range of columns or rows.
|
* A beginning and end index for a range of columns or rows.
|
||||||
@ -266,7 +266,7 @@ export class SelectionSummary extends Disposable {
|
|||||||
|
|
||||||
async function doCopy(value: string, elem: Element) {
|
async function doCopy(value: string, elem: Element) {
|
||||||
await copyToClipboard(value);
|
await copyToClipboard(value);
|
||||||
showTransientTooltip(elem, t('CopiedClipboard'), {key: 'copy-selection-summary'});
|
showTransientTooltip(elem, t("Copied to clipboard"), {key: 'copy-selection-summary'});
|
||||||
}
|
}
|
||||||
|
|
||||||
const cssSummary = styled('div', `
|
const cssSummary = styled('div', `
|
||||||
|
@ -19,7 +19,7 @@ import {UserAction} from 'app/common/DocActions';
|
|||||||
import {Computed, dom, fromKo, Observable} from 'grainjs';
|
import {Computed, dom, fromKo, Observable} from 'grainjs';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('components.TypeTransformation');
|
const t = makeT('TypeTransformation');
|
||||||
|
|
||||||
// To simplify diff (avoid rearranging methods to satisfy private/public order).
|
// To simplify diff (avoid rearranging methods to satisfy private/public order).
|
||||||
/* eslint-disable @typescript-eslint/member-ordering */
|
/* eslint-disable @typescript-eslint/member-ordering */
|
||||||
@ -72,7 +72,7 @@ export class TypeTransform extends ColumnTransform {
|
|||||||
return basicButton(dom.on('click', () => this.editor.writeObservable()),
|
return basicButton(dom.on('click', () => this.editor.writeObservable()),
|
||||||
t('Preview'), testId("type-transform-update"),
|
t('Preview'), testId("type-transform-update"),
|
||||||
dom.cls('disabled', (use) => use(disableButtons) || use(this.formulaUpToDate)),
|
dom.cls('disabled', (use) => use(disableButtons) || use(this.formulaUpToDate)),
|
||||||
{ title: t('UpdateFormula') }
|
{ title: t('Update formula (Shift+Enter)') }
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return basicButton(dom.on('click', () => { this._reviseTypeChange.set(true); }),
|
return basicButton(dom.on('click', () => { this._reviseTypeChange.set(true); }),
|
||||||
|
@ -7,7 +7,7 @@ var kf = require('../lib/koForm');
|
|||||||
var AceEditor = require('./AceEditor');
|
var AceEditor = require('./AceEditor');
|
||||||
var {makeT} = require('app/client/lib/localization');
|
var {makeT} = require('app/client/lib/localization');
|
||||||
|
|
||||||
const t = makeT('components.ValidationPanel');
|
const t = makeT('ValidationPanel');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document level configuration settings.
|
* Document level configuration settings.
|
||||||
@ -33,7 +33,7 @@ dispose.makeDisposable(ValidationPanel);
|
|||||||
ValidationPanel.prototype.onAddRule = function() {
|
ValidationPanel.prototype.onAddRule = function() {
|
||||||
this.validationsTable.sendTableAction(["AddRecord", null, {
|
this.validationsTable.sendTableAction(["AddRecord", null, {
|
||||||
tableRef: this.docTables.at(0).id(),
|
tableRef: this.docTables.at(0).id(),
|
||||||
name: t("RuleLength", {length: this.validations.peekLength + 1}),
|
name: t("Rule {{length}}", {length: this.validations.peekLength + 1}),
|
||||||
formula: ""
|
formula: ""
|
||||||
}])
|
}])
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -86,7 +86,7 @@ ValidationPanel.prototype.buildDom = function() {
|
|||||||
2, '',
|
2, '',
|
||||||
1, kf.buttonGroup(
|
1, kf.buttonGroup(
|
||||||
kf.button(() => editor.writeObservable(),
|
kf.button(() => editor.writeObservable(),
|
||||||
'Apply', { title: t('UpdateFormula')},
|
'Apply', { title: t("Update formula (Shift+Enter)")},
|
||||||
kd.toggleClass('disabled', editorUpToDate)
|
kd.toggleClass('disabled', editorUpToDate)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,7 @@ const {makeT} = require('app/client/lib/localization');
|
|||||||
|
|
||||||
const testId = makeTestId('test-vconfigtab-');
|
const testId = makeTestId('test-vconfigtab-');
|
||||||
|
|
||||||
const t = makeT('components.ViewConfigTab');
|
const t = makeT('ViewConfigTab');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class that combines one ViewSection's data for building dom.
|
* Helper class that combines one ViewSection's data for building dom.
|
||||||
@ -124,9 +124,9 @@ ViewConfigTab.prototype._buildAdvancedSettingsDom = function() {
|
|||||||
const table = sectionData.section.table();
|
const table = sectionData.section.table();
|
||||||
const isCollapsed = ko.observable(true);
|
const isCollapsed = ko.observable(true);
|
||||||
return [
|
return [
|
||||||
kf.collapserLabel(isCollapsed, t('AdvancedSettings'), dom.testId('ViewConfig_advanced')),
|
kf.collapserLabel(isCollapsed, t("Advanced settings"), dom.testId('ViewConfig_advanced')),
|
||||||
kf.helpRow(kd.hide(isCollapsed),
|
kf.helpRow(kd.hide(isCollapsed),
|
||||||
t('BigTablesMayBeMarked'),
|
t("Big tables may be marked as \"on-demand\" to avoid loading them into the data engine."),
|
||||||
kd.style('text-align', 'left'),
|
kd.style('text-align', 'left'),
|
||||||
kd.style('margin-top', '1.5rem')
|
kd.style('margin-top', '1.5rem')
|
||||||
),
|
),
|
||||||
@ -135,7 +135,7 @@ ViewConfigTab.prototype._buildAdvancedSettingsDom = function() {
|
|||||||
),
|
),
|
||||||
kf.row(kd.hide(isCollapsed),
|
kf.row(kd.hide(isCollapsed),
|
||||||
kf.buttonGroup(kf.button(() => this._makeOnDemand(table),
|
kf.buttonGroup(kf.button(() => this._makeOnDemand(table),
|
||||||
kd.text(() => table.onDemand() ? t('UnmarkOnDemandButton') : t('MakeOnDemandButton')),
|
kd.text(() => table.onDemand() ? t("Unmark On-Demand") : t("Make On-Demand")),
|
||||||
dom.testId('ViewConfig_onDemandBtn')
|
dom.testId('ViewConfig_onDemandBtn')
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
@ -152,9 +152,9 @@ ViewConfigTab.prototype._buildThemeDom = function() {
|
|||||||
return cssRow(
|
return cssRow(
|
||||||
dom.autoDispose(theme),
|
dom.autoDispose(theme),
|
||||||
select(theme, [
|
select(theme, [
|
||||||
{label: t('Form'), value: 'form' },
|
{label: t("Form"), value: 'form' },
|
||||||
{label: t('Compact'), value: 'compact'},
|
{label: t("Compact"), value: 'compact'},
|
||||||
{label: t('Blocks'), value: 'blocks' },
|
{label: t("Blocks"), value: 'blocks' },
|
||||||
]),
|
]),
|
||||||
testId('detail-theme')
|
testId('detail-theme')
|
||||||
);
|
);
|
||||||
@ -173,7 +173,7 @@ ViewConfigTab.prototype._buildLayoutDom = function() {
|
|||||||
const layoutEditorObs = ko.computed(() => view && view.recordLayout && view.recordLayout.layoutEditor());
|
const layoutEditorObs = ko.computed(() => view && view.recordLayout && view.recordLayout.layoutEditor());
|
||||||
return cssRow({style: 'margin-top: 16px;'},
|
return cssRow({style: 'margin-top: 16px;'},
|
||||||
kd.maybe(layoutEditorObs, (editor) => editor.buildFinishButtons()),
|
kd.maybe(layoutEditorObs, (editor) => editor.buildFinishButtons()),
|
||||||
primaryButton(t('EditCardLayout'),
|
primaryButton(t("Edit Card Layout"),
|
||||||
dom.autoDispose(layoutEditorObs),
|
dom.autoDispose(layoutEditorObs),
|
||||||
dom.on('click', () => commands.allCommands.editLayout.run()),
|
dom.on('click', () => commands.allCommands.editLayout.run()),
|
||||||
grainjsDom.hide(layoutEditorObs),
|
grainjsDom.hide(layoutEditorObs),
|
||||||
@ -222,8 +222,8 @@ ViewConfigTab.prototype._buildCustomTypeItems = function() {
|
|||||||
// 3)
|
// 3)
|
||||||
showObs: () => activeSection().customDef.mode() === "plugin",
|
showObs: () => activeSection().customDef.mode() === "plugin",
|
||||||
buildDom: () => kd.scope(activeSection, ({customDef}) => dom('div',
|
buildDom: () => kd.scope(activeSection, ({customDef}) => dom('div',
|
||||||
kf.row(5, t("PluginColon"), 13, kf.text(customDef.pluginId, {}, {list: "list_plugin"}, dom.testId('ViewConfigTab_customView_pluginId'))),
|
kf.row(5, t("Plugin: "), 13, kf.text(customDef.pluginId, {}, {list: "list_plugin"}, dom.testId('ViewConfigTab_customView_pluginId'))),
|
||||||
kf.row(5, t("SectionColon"), 13, kf.text(customDef.sectionId, {}, {list: "list_section"}, dom.testId('ViewConfigTab_customView_sectionId'))),
|
kf.row(5, t("Section: "), 13, kf.text(customDef.sectionId, {}, {list: "list_section"}, dom.testId('ViewConfigTab_customView_sectionId'))),
|
||||||
// For both `customPlugin` and `selectedSection` it is possible for the value not to be in the
|
// For both `customPlugin` and `selectedSection` it is possible for the value not to be in the
|
||||||
// list of options. Combining <datalist> and <input> allows both to freely edit the value with
|
// list of options. Combining <datalist> and <input> allows both to freely edit the value with
|
||||||
// keyboard and to select it from a list. Although the content of the list seems to be
|
// keyboard and to select it from a list. Although the content of the list seems to be
|
||||||
|
@ -15,7 +15,7 @@ import zip = require('lodash/zip');
|
|||||||
import zipObject = require('lodash/zipObject');
|
import zipObject = require('lodash/zipObject');
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('components.duplicatePage');
|
const t = makeT('duplicatePage');
|
||||||
|
|
||||||
// Duplicate page with pageId. Starts by prompting user for a new name.
|
// Duplicate page with pageId. Starts by prompting user for a new name.
|
||||||
export async function duplicatePage(gristDoc: GristDoc, pageId: number) {
|
export async function duplicatePage(gristDoc: GristDoc, pageId: number) {
|
||||||
@ -30,7 +30,7 @@ export async function duplicatePage(gristDoc: GristDoc, pageId: number) {
|
|||||||
cssLabel("Name"),
|
cssLabel("Name"),
|
||||||
inputEl = cssInput({value: pageName + ' (copy)'}),
|
inputEl = cssInput({value: pageName + ' (copy)'}),
|
||||||
),
|
),
|
||||||
t("DoesNotCopyData"),
|
t("Note that this does not copy data, but creates another view of the same data."),
|
||||||
])
|
])
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ async function makeDuplicate(gristDoc: GristDoc, pageId: number, pageName: strin
|
|||||||
const viewSections = sourceView.viewSections.peek().peek();
|
const viewSections = sourceView.viewSections.peek().peek();
|
||||||
let viewRef = 0;
|
let viewRef = 0;
|
||||||
await gristDoc.docData.bundleActions(
|
await gristDoc.docData.bundleActions(
|
||||||
t("DuplicatePageName", {pageName}),
|
t("Duplicate page {{pageName}}", {pageName}),
|
||||||
async () => {
|
async () => {
|
||||||
// create new view and new sections
|
// create new view and new sections
|
||||||
const results = await createNewViewSections(gristDoc.docData, viewSections);
|
const results = await createNewViewSections(gristDoc.docData, viewSections);
|
||||||
|
@ -18,7 +18,7 @@ import {createUserImage, cssUserImage} from "app/client/ui/UserImage";
|
|||||||
import {Computed, computed, dom, DomElementArg, Holder, IDisposableOwner, Observable, styled} from "grainjs";
|
import {Computed, computed, dom, DomElementArg, Holder, IDisposableOwner, Observable, styled} from "grainjs";
|
||||||
import {cssMenuItem} from "popweasel";
|
import {cssMenuItem} from "popweasel";
|
||||||
|
|
||||||
const t = makeT('lib.ACUserManager');
|
const t = makeT('ACUserManager');
|
||||||
|
|
||||||
export interface ACUserItem extends ACItem {
|
export interface ACUserItem extends ACItem {
|
||||||
value: string;
|
value: string;
|
||||||
@ -109,10 +109,10 @@ export function buildACMemberEmail(
|
|||||||
cssUserImagePlus.cls('-invalid', (use) => !use(enableAdd),
|
cssUserImagePlus.cls('-invalid', (use) => !use(enableAdd),
|
||||||
)),
|
)),
|
||||||
cssMemberText(
|
cssMemberText(
|
||||||
cssMemberPrimaryPlus(t("InviteNewMember")),
|
cssMemberPrimaryPlus(t("Invite new member")),
|
||||||
cssMemberSecondaryPlus(
|
cssMemberSecondaryPlus(
|
||||||
// dom.text(use => `We'll email an invite to ${use(emailObs)}`)
|
// dom.text(use => `We'll email an invite to ${use(emailObs)}`)
|
||||||
dom.text(use => t('InviteEmail', {email: use(emailObs)})) // TODO i18next
|
dom.text(use => t("We'll email an invite to {{email}}", {email: use(emailObs)})) // TODO i18next
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
testId("um-add-email")
|
testId("um-add-email")
|
||||||
@ -143,7 +143,7 @@ export function buildACMemberEmail(
|
|||||||
(emailInput = cssEmailInput(
|
(emailInput = cssEmailInput(
|
||||||
emailObs,
|
emailObs,
|
||||||
{onInput: true, isValid},
|
{onInput: true, isValid},
|
||||||
{type: "email", placeholder: t("EmailInputPlaceholder")},
|
{type: "email", placeholder: t("Enter email address")},
|
||||||
dom.on("input", acOpen),
|
dom.on("input", acOpen),
|
||||||
dom.on("focus", acOpen),
|
dom.on("focus", acOpen),
|
||||||
dom.on("click", acOpen),
|
dom.on("click", acOpen),
|
||||||
|
@ -24,7 +24,7 @@ import {getOrgName, Organization, OrgError, UserAPI, UserAPIImpl} from 'app/comm
|
|||||||
import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs';
|
import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs';
|
||||||
import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs';
|
import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs';
|
||||||
|
|
||||||
const t = makeT('models.AppModel');
|
const t = makeT('AppModel');
|
||||||
|
|
||||||
// Reexported for convenience.
|
// Reexported for convenience.
|
||||||
export {reportError} from 'app/client/models/errors';
|
export {reportError} from 'app/client/models/errors';
|
||||||
@ -199,7 +199,7 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
|
|||||||
if (org.billingAccount && org.billingAccount.product &&
|
if (org.billingAccount && org.billingAccount.product &&
|
||||||
org.billingAccount.product.name === 'suspended') {
|
org.billingAccount.product.name === 'suspended') {
|
||||||
this.notifier.createUserMessage(
|
this.notifier.createUserMessage(
|
||||||
t('TeamSiteSuspended'),
|
t("This team site is suspended. Documents can be read, but not modified."),
|
||||||
{actions: ['renew', 'personal']}
|
{actions: ['renew', 'personal']}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import {makeT} from 'app/client/lib/localization';
|
|||||||
|
|
||||||
// tslint:disable:no-console
|
// tslint:disable:no-console
|
||||||
|
|
||||||
const t = makeT('models.DocPageModel');
|
const t = makeT('DocPageModel');
|
||||||
|
|
||||||
export interface DocInfo extends Document {
|
export interface DocInfo extends Document {
|
||||||
isReadonly: boolean;
|
isReadonly: boolean;
|
||||||
@ -236,13 +236,14 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
|
|||||||
const isDenied = (err as any).code === 'ACL_DENY';
|
const isDenied = (err as any).code === 'ACL_DENY';
|
||||||
const isDocOwner = isOwner(this.currentDoc.get());
|
const isDocOwner = isOwner(this.currentDoc.get());
|
||||||
confirmModal(
|
confirmModal(
|
||||||
t("ErrorAccessingDocument"),
|
t("Error accessing document"),
|
||||||
t("Reload"),
|
t("Reload"),
|
||||||
async () => window.location.reload(true),
|
async () => window.location.reload(true),
|
||||||
isDocOwner ? t('ReloadingOrRecoveryMode', {error: err.message}) :
|
isDocOwner ? t("You can try reloading the document, or using recovery mode. Recovery mode opens the document to be fully accessible to owners, and inaccessible to others. It also disables formulas. [{{error}}]", {error: err.message}) :
|
||||||
t('AccessError', {context: isDenied ? 'denied' : 'recover', error: err.message}),
|
isDenied ? t('Sorry, access to this document has been denied. [{{error}}]', {error: err.message}) :
|
||||||
|
t("Document owners can attempt to recover the document. [{{error}}]", {error: err.message}),
|
||||||
{ hideCancel: true,
|
{ hideCancel: true,
|
||||||
extraButtons: (isDocOwner && !isDenied) ? bigBasicButton(t('EnterRecoveryMode'), dom.on('click', async () => {
|
extraButtons: (isDocOwner && !isDenied) ? bigBasicButton(t("Enter recovery mode"), dom.on('click', async () => {
|
||||||
await this._api.getDocAPI(this.currentDocId.get()!).recover(true);
|
await this._api.getDocAPI(this.currentDocId.get()!).recover(true);
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
}), testId('modal-recovery-mode')) : null,
|
}), testId('modal-recovery-mode')) : null,
|
||||||
@ -338,18 +339,18 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
|
|||||||
menuItem(
|
menuItem(
|
||||||
(elem) => openPageWidgetPicker(elem, gristDoc, (val) => gristDoc.addNewPage(val).catch(reportError),
|
(elem) => openPageWidgetPicker(elem, gristDoc, (val) => gristDoc.addNewPage(val).catch(reportError),
|
||||||
{isNewPage: true, buttonLabel: 'Add Page'}),
|
{isNewPage: true, buttonLabel: 'Add Page'}),
|
||||||
menuIcon("Page"), t("AddPage"), testId('dp-add-new-page'),
|
menuIcon("Page"), t("Add Page"), testId('dp-add-new-page'),
|
||||||
dom.cls('disabled', isReadonly)
|
dom.cls('disabled', isReadonly)
|
||||||
),
|
),
|
||||||
menuItem(
|
menuItem(
|
||||||
(elem) => openPageWidgetPicker(elem, gristDoc, (val) => gristDoc.addWidgetToPage(val).catch(reportError),
|
(elem) => openPageWidgetPicker(elem, gristDoc, (val) => gristDoc.addWidgetToPage(val).catch(reportError),
|
||||||
{isNewPage: false, selectBy}),
|
{isNewPage: false, selectBy}),
|
||||||
menuIcon("Widget"), t("AddWidgetToPage"), testId('dp-add-widget-to-page'),
|
menuIcon("Widget"), t("Add Widget to Page"), testId('dp-add-widget-to-page'),
|
||||||
// disable for readonly doc and all special views
|
// disable for readonly doc and all special views
|
||||||
dom.cls('disabled', (use) => typeof use(gristDoc.activeViewId) !== 'number' || isReadonly),
|
dom.cls('disabled', (use) => typeof use(gristDoc.activeViewId) !== 'number' || isReadonly),
|
||||||
),
|
),
|
||||||
menuItem(() => gristDoc.addEmptyTable().catch(reportError),
|
menuItem(() => gristDoc.addEmptyTable().catch(reportError),
|
||||||
menuIcon("TypeTable"), t("AddEmptyTable"), testId('dp-empty-table'),
|
menuIcon("TypeTable"), t("Add Empty Table"), testId('dp-empty-table'),
|
||||||
dom.cls('disabled', isReadonly)
|
dom.cls('disabled', isReadonly)
|
||||||
),
|
),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
@ -361,7 +362,7 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
|
|||||||
dom.cls('disabled', isReadonly)
|
dom.cls('disabled', isReadonly)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
isReadonly ? menuText(t('NoEditAccess')) : null,
|
isReadonly ? menuText(t("You do not have edit access to this document")) : null,
|
||||||
testId('dp-add-new-menu')
|
testId('dp-add-new-menu')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {ANONYMOUS_USER_EMAIL, Document, EVERYONE_EMAIL, FullUser, getRealAccess,
|
|||||||
import {computed, Computed, Disposable, obsArray, ObsArray, observable, Observable} from 'grainjs';
|
import {computed, Computed, Disposable, obsArray, ObsArray, observable, Observable} from 'grainjs';
|
||||||
import some = require('lodash/some');
|
import some = require('lodash/some');
|
||||||
|
|
||||||
const t = makeT('models.UserManagerModel');
|
const t = makeT('UserManagerModel');
|
||||||
|
|
||||||
export interface UserManagerModel {
|
export interface UserManagerModel {
|
||||||
initData: PermissionData; // PermissionData used to initialize the UserManager
|
initData: PermissionData; // PermissionData used to initialize the UserManager
|
||||||
@ -101,28 +101,28 @@ interface IBuildMemberOptions {
|
|||||||
export class UserManagerModelImpl extends Disposable implements UserManagerModel {
|
export class UserManagerModelImpl extends Disposable implements UserManagerModel {
|
||||||
// Select options for each individual user's role dropdown.
|
// Select options for each individual user's role dropdown.
|
||||||
public readonly userSelectOptions: IMemberSelectOption[] = [
|
public readonly userSelectOptions: IMemberSelectOption[] = [
|
||||||
{ value: roles.OWNER, label: t('Owner') },
|
{ value: roles.OWNER, label: t("Owner") },
|
||||||
{ value: roles.EDITOR, label: t('Editor') },
|
{ value: roles.EDITOR, label: t("Editor") },
|
||||||
{ value: roles.VIEWER, label: t('Viewer') }
|
{ value: roles.VIEWER, label: t("Viewer") }
|
||||||
];
|
];
|
||||||
// Select options for each individual user's role dropdown in the org.
|
// Select options for each individual user's role dropdown in the org.
|
||||||
public readonly orgUserSelectOptions: IOrgMemberSelectOption[] = [
|
public readonly orgUserSelectOptions: IOrgMemberSelectOption[] = [
|
||||||
{ value: roles.OWNER, label: t('Owner') },
|
{ value: roles.OWNER, label: t("Owner") },
|
||||||
{ value: roles.EDITOR, label: t('Editor') },
|
{ value: roles.EDITOR, label: t("Editor") },
|
||||||
{ value: roles.VIEWER, label: t('Viewer') },
|
{ value: roles.VIEWER, label: t("Viewer") },
|
||||||
{ value: roles.MEMBER, label: t('NoDefaultAccess') },
|
{ value: roles.MEMBER, label: t("No Default Access") },
|
||||||
];
|
];
|
||||||
// Select options for the resource's maxInheritedRole dropdown.
|
// Select options for the resource's maxInheritedRole dropdown.
|
||||||
public readonly inheritSelectOptions: IMemberSelectOption[] = [
|
public readonly inheritSelectOptions: IMemberSelectOption[] = [
|
||||||
{ value: roles.OWNER, label: t('InFull') },
|
{ value: roles.OWNER, label: t("In Full") },
|
||||||
{ value: roles.EDITOR, label: t('ViewAndEdit') },
|
{ value: roles.EDITOR, label: t("View & Edit") },
|
||||||
{ value: roles.VIEWER, label: t('ViewOnly') },
|
{ value: roles.VIEWER, label: t("View Only") },
|
||||||
{ value: null, label: t('None') }
|
{ value: null, label: t("None") }
|
||||||
];
|
];
|
||||||
// Select options for the public member's role dropdown.
|
// Select options for the public member's role dropdown.
|
||||||
public readonly publicUserSelectOptions: IMemberSelectOption[] = [
|
public readonly publicUserSelectOptions: IMemberSelectOption[] = [
|
||||||
{ value: roles.EDITOR, label: t('Editor') },
|
{ value: roles.EDITOR, label: t("Editor") },
|
||||||
{ value: roles.VIEWER, label: t('Viewer') },
|
{ value: roles.VIEWER, label: t("Viewer") },
|
||||||
];
|
];
|
||||||
|
|
||||||
public activeUser: FullUser|null = this._options.activeUser ?? null;
|
public activeUser: FullUser|null = this._options.activeUser ?? null;
|
||||||
|
@ -58,13 +58,13 @@ export class AccountPage extends Disposable {
|
|||||||
const {enableCustomCss} = getGristConfig();
|
const {enableCustomCss} = getGristConfig();
|
||||||
return domComputed(this._userObs, (user) => user && (
|
return domComputed(this._userObs, (user) => user && (
|
||||||
css.container(css.accountPage(
|
css.container(css.accountPage(
|
||||||
css.header(t('AccountSettings')),
|
css.header(t("Account settings")),
|
||||||
css.dataRow(
|
css.dataRow(
|
||||||
css.inlineSubHeader(t('Email')),
|
css.inlineSubHeader(t("Email")),
|
||||||
css.email(user.email),
|
css.email(user.email),
|
||||||
),
|
),
|
||||||
css.dataRow(
|
css.dataRow(
|
||||||
css.inlineSubHeader(t('Name')),
|
css.inlineSubHeader(t("Name")),
|
||||||
domComputed(this._isEditingName, (isEditing) => (
|
domComputed(this._isEditingName, (isEditing) => (
|
||||||
isEditing ? [
|
isEditing ? [
|
||||||
transientInput(
|
transientInput(
|
||||||
@ -78,13 +78,13 @@ export class AccountPage extends Disposable {
|
|||||||
css.flexGrow.cls(''),
|
css.flexGrow.cls(''),
|
||||||
),
|
),
|
||||||
css.textBtn(
|
css.textBtn(
|
||||||
css.icon('Settings'), t('Save'),
|
css.icon('Settings'), t("Save"),
|
||||||
// No need to save on 'click'. The transient input already does it on close.
|
// No need to save on 'click'. The transient input already does it on close.
|
||||||
),
|
),
|
||||||
] : [
|
] : [
|
||||||
css.name(user.name),
|
css.name(user.name),
|
||||||
css.textBtn(
|
css.textBtn(
|
||||||
css.icon('Settings'), t('Edit'),
|
css.icon('Settings'), t("Edit"),
|
||||||
dom.on('click', () => this._isEditingName.set(true)),
|
dom.on('click', () => this._isEditingName.set(true)),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -93,11 +93,11 @@ export class AccountPage extends Disposable {
|
|||||||
),
|
),
|
||||||
// show warning for invalid name but not for the empty string
|
// show warning for invalid name but not for the empty string
|
||||||
dom.maybe(use => use(this._nameEdit) && !use(this._isNameValid), cssWarnings),
|
dom.maybe(use => use(this._nameEdit) && !use(this._isNameValid), cssWarnings),
|
||||||
css.header(t('PasswordSecurity')),
|
css.header(t("Password & Security")),
|
||||||
css.dataRow(
|
css.dataRow(
|
||||||
css.inlineSubHeader(t("LoginMethod")),
|
css.inlineSubHeader(t("Login Method")),
|
||||||
css.loginMethod(user.loginMethod),
|
css.loginMethod(user.loginMethod),
|
||||||
user.loginMethod === 'Email + Password' ? css.textBtn(t("ChangePassword"),
|
user.loginMethod === 'Email + Password' ? css.textBtn(t("Change Password"),
|
||||||
dom.on('click', () => this._showChangePasswordDialog()),
|
dom.on('click', () => this._showChangePasswordDialog()),
|
||||||
) : null,
|
) : null,
|
||||||
testId('login-method'),
|
testId('login-method'),
|
||||||
@ -106,24 +106,24 @@ export class AccountPage extends Disposable {
|
|||||||
css.dataRow(
|
css.dataRow(
|
||||||
labeledSquareCheckbox(
|
labeledSquareCheckbox(
|
||||||
this._allowGoogleLogin,
|
this._allowGoogleLogin,
|
||||||
t('AllowGoogleSigning'),
|
t("Allow signing in to this account with Google"),
|
||||||
testId('allow-google-login-checkbox'),
|
testId('allow-google-login-checkbox'),
|
||||||
),
|
),
|
||||||
testId('allow-google-login'),
|
testId('allow-google-login'),
|
||||||
),
|
),
|
||||||
css.subHeader(t('TwoFactorAuth')),
|
css.subHeader(t("Two-factor authentication")),
|
||||||
css.description(
|
css.description(
|
||||||
t("TwoFactorAuthDescription")
|
t("Two-factor authentication is an extra layer of security for your Grist account designed to ensure that you're the only person who can access your account, even if someone knows your password.")
|
||||||
),
|
),
|
||||||
dom.create(MFAConfig, user),
|
dom.create(MFAConfig, user),
|
||||||
),
|
),
|
||||||
// Custom CSS is incompatible with custom themes.
|
// Custom CSS is incompatible with custom themes.
|
||||||
enableCustomCss ? null : [
|
enableCustomCss ? null : [
|
||||||
css.header(t('Theme')),
|
css.header(t("Theme")),
|
||||||
dom.create(ThemeConfig, this._appModel),
|
dom.create(ThemeConfig, this._appModel),
|
||||||
],
|
],
|
||||||
css.header(t('API')),
|
css.header(t("API")),
|
||||||
css.dataRow(css.inlineSubHeader(t('APIKey')), css.content(
|
css.dataRow(css.inlineSubHeader(t("API Key")), css.content(
|
||||||
dom.create(ApiKey, {
|
dom.create(ApiKey, {
|
||||||
apiKey: this._apiKey,
|
apiKey: this._apiKey,
|
||||||
onCreate: () => this._createApiKey(),
|
onCreate: () => this._createApiKey(),
|
||||||
@ -214,7 +214,7 @@ export function checkName(name: string): boolean {
|
|||||||
*/
|
*/
|
||||||
function buildNameWarningsDom() {
|
function buildNameWarningsDom() {
|
||||||
return css.warning(
|
return css.warning(
|
||||||
t("WarningUsername"),
|
t("Names only allow letters, numbers and certain special characters"),
|
||||||
testId('username-warning'),
|
testId('username-warning'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ export class AccountWidget extends Disposable {
|
|||||||
cssUserIcon(createUserImage(user, 'medium', testId('user-icon')),
|
cssUserIcon(createUserImage(user, 'medium', testId('user-icon')),
|
||||||
menu(() => this._makeAccountMenu(user), {placement: 'bottom-end'}),
|
menu(() => this._makeAccountMenu(user), {placement: 'bottom-end'}),
|
||||||
) :
|
) :
|
||||||
cssSignInButton(t('SignIn'), icon('Collapse'), testId('user-signin'),
|
cssSignInButton(t("Sign in"), icon('Collapse'), testId('user-signin'),
|
||||||
menu(() => this._makeAccountMenu(user), {placement: 'bottom-end'}),
|
menu(() => this._makeAccountMenu(user), {placement: 'bottom-end'}),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -57,24 +57,24 @@ export class AccountWidget extends Disposable {
|
|||||||
// The 'Document Settings' item, when there is an open document.
|
// The 'Document Settings' item, when there is an open document.
|
||||||
const documentSettingsItem = (gristDoc ?
|
const documentSettingsItem = (gristDoc ?
|
||||||
menuItem(async () => (await loadGristDoc()).showDocSettingsModal(gristDoc.docInfo, this._docPageModel!),
|
menuItem(async () => (await loadGristDoc()).showDocSettingsModal(gristDoc.docInfo, this._docPageModel!),
|
||||||
t('DocumentSettings'),
|
t("Document Settings"),
|
||||||
testId('dm-doc-settings')) :
|
testId('dm-doc-settings')) :
|
||||||
null);
|
null);
|
||||||
|
|
||||||
// The item to toggle mobile mode (presence of viewport meta tag).
|
// The item to toggle mobile mode (presence of viewport meta tag).
|
||||||
const mobileModeToggle = menuItem(viewport.toggleViewport,
|
const mobileModeToggle = menuItem(viewport.toggleViewport,
|
||||||
cssSmallDeviceOnly.cls(''), // Only show this toggle on small devices.
|
cssSmallDeviceOnly.cls(''), // Only show this toggle on small devices.
|
||||||
t('ToggleMobileMode'),
|
t("Toggle Mobile Mode"),
|
||||||
cssCheckmark('Tick', dom.show(viewport.viewportEnabled)),
|
cssCheckmark('Tick', dom.show(viewport.viewportEnabled)),
|
||||||
testId('usermenu-toggle-mobile'),
|
testId('usermenu-toggle-mobile'),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return [
|
return [
|
||||||
menuItemLink({href: getLoginOrSignupUrl()}, t('SignIn')),
|
menuItemLink({href: getLoginOrSignupUrl()}, t("Sign in")),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
documentSettingsItem,
|
documentSettingsItem,
|
||||||
menuItemLink({href: commonUrls.plans}, t('Pricing')),
|
menuItemLink({href: commonUrls.plans}, t("Pricing")),
|
||||||
mobileModeToggle,
|
mobileModeToggle,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -88,14 +88,14 @@ export class AccountWidget extends Disposable {
|
|||||||
cssEmail(user.email, testId('usermenu-email'))
|
cssEmail(user.email, testId('usermenu-email'))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
menuItemLink(urlState().setLinkUrl({account: 'account'}), t('ProfileSettings')),
|
menuItemLink(urlState().setLinkUrl({account: 'account'}), t("Profile Settings")),
|
||||||
|
|
||||||
documentSettingsItem,
|
documentSettingsItem,
|
||||||
|
|
||||||
// Show 'Organization Settings' when on a home page of a valid org.
|
// Show 'Organization Settings' when on a home page of a valid org.
|
||||||
(!this._docPageModel && currentOrg && this._appModel.isTeamSite ?
|
(!this._docPageModel && currentOrg && this._appModel.isTeamSite ?
|
||||||
menuItem(() => manageTeamUsers(currentOrg, user, this._appModel.api),
|
menuItem(() => manageTeamUsers(currentOrg, user, this._appModel.api),
|
||||||
roles.canEditAccess(currentOrg.access) ? t('ManageTeam') : t('AccessDetails'),
|
roles.canEditAccess(currentOrg.access) ? t("Manage Team") : t("Access Details"),
|
||||||
testId('dm-org-access')) :
|
testId('dm-org-access')) :
|
||||||
// Don't show on doc pages, or for personal orgs.
|
// Don't show on doc pages, or for personal orgs.
|
||||||
null),
|
null),
|
||||||
@ -111,7 +111,7 @@ export class AccountWidget extends Disposable {
|
|||||||
// org-listing UI below.
|
// org-listing UI below.
|
||||||
this._appModel.topAppModel.isSingleOrg || shouldHideUiElement("multiAccounts") ? [] : [
|
this._appModel.topAppModel.isSingleOrg || shouldHideUiElement("multiAccounts") ? [] : [
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuSubHeader(dom.text((use) => use(users).length > 1 ? t('SwitchAccounts') : t('Accounts'))),
|
menuSubHeader(dom.text((use) => use(users).length > 1 ? t("Switch Accounts") : t("Accounts"))),
|
||||||
dom.forEach(users, (_user) => {
|
dom.forEach(users, (_user) => {
|
||||||
if (_user.id === user.id) { return null; }
|
if (_user.id === user.id) { return null; }
|
||||||
return menuItem(() => this._switchAccount(_user),
|
return menuItem(() => this._switchAccount(_user),
|
||||||
@ -119,10 +119,10 @@ export class AccountWidget extends Disposable {
|
|||||||
cssOtherEmail(_user.email, testId('usermenu-other-email')),
|
cssOtherEmail(_user.email, testId('usermenu-other-email')),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
isExternal ? null : menuItemLink({href: getLoginUrl()}, t("AddAccount"), testId('dm-add-account')),
|
isExternal ? null : menuItemLink({href: getLoginUrl()}, t("Add Account"), testId('dm-add-account')),
|
||||||
],
|
],
|
||||||
|
|
||||||
menuItemLink({href: getLogoutUrl()}, t("SignOut"), testId('dm-log-out')),
|
menuItemLink({href: getLogoutUrl()}, t("Sign Out"), testId('dm-log-out')),
|
||||||
|
|
||||||
maybeAddSiteSwitcherSection(this._appModel),
|
maybeAddSiteSwitcherSection(this._appModel),
|
||||||
];
|
];
|
||||||
|
@ -10,7 +10,7 @@ export function addNewButton(isOpen: Observable<boolean> | boolean = true, ...ar
|
|||||||
cssAddNewButton.cls('-open', isOpen),
|
cssAddNewButton.cls('-open', isOpen),
|
||||||
// Setting spacing as flex items allows them to shrink faster when there isn't enough space.
|
// Setting spacing as flex items allows them to shrink faster when there isn't enough space.
|
||||||
cssLeftMargin(),
|
cssLeftMargin(),
|
||||||
cssAddText(t('AddNew')),
|
cssAddText(t("Add New")),
|
||||||
dom('div', {style: 'flex: 1 1 16px'}),
|
dom('div', {style: 'flex: 1 1 16px'}),
|
||||||
cssPlusButton(cssPlusIcon('Plus')),
|
cssPlusButton(cssPlusIcon('Plus')),
|
||||||
dom('div', {style: 'flex: 0 1 16px'}),
|
dom('div', {style: 'flex: 0 1 16px'}),
|
||||||
|
@ -55,7 +55,7 @@ export class ApiKey extends Disposable {
|
|||||||
},
|
},
|
||||||
dom.attr('type', (use) => use(this._isHidden) ? 'password' : 'text'),
|
dom.attr('type', (use) => use(this._isHidden) ? 'password' : 'text'),
|
||||||
testId('key'),
|
testId('key'),
|
||||||
{title: t('ClickToShow')},
|
{title: t("Click to show")},
|
||||||
dom.on('click', (_ev, el) => {
|
dom.on('click', (_ev, el) => {
|
||||||
this._isHidden.set(false);
|
this._isHidden.set(false);
|
||||||
setTimeout(() => el.select(), 0);
|
setTimeout(() => el.select(), 0);
|
||||||
@ -67,7 +67,7 @@ export class ApiKey extends Disposable {
|
|||||||
this._inputArgs
|
this._inputArgs
|
||||||
),
|
),
|
||||||
cssTextBtn(
|
cssTextBtn(
|
||||||
cssTextBtnIcon('Remove'), t('Remove'),
|
cssTextBtnIcon('Remove'), t("Remove"),
|
||||||
dom.on('click', () => this._showRemoveKeyModal()),
|
dom.on('click', () => this._showRemoveKeyModal()),
|
||||||
testId('delete'),
|
testId('delete'),
|
||||||
dom.boolAttr('disabled', (use) => use(this._loading) || this._anonymous)
|
dom.boolAttr('disabled', (use) => use(this._loading) || this._anonymous)
|
||||||
@ -76,9 +76,9 @@ export class ApiKey extends Disposable {
|
|||||||
description(this._getDescription(), testId('description')),
|
description(this._getDescription(), testId('description')),
|
||||||
)),
|
)),
|
||||||
dom.maybe((use) => !(use(this._apiKey) || this._anonymous), () => [
|
dom.maybe((use) => !(use(this._apiKey) || this._anonymous), () => [
|
||||||
basicButton(t('Create'), dom.on('click', () => this._onCreate()), testId('create'),
|
basicButton(t("Create"), dom.on('click', () => this._onCreate()), testId('create'),
|
||||||
dom.boolAttr('disabled', this._loading)),
|
dom.boolAttr('disabled', this._loading)),
|
||||||
description(t('ByGenerating'), testId('description')),
|
description(t("By generating an API key, you will be able to make API calls for your own account."), testId('description')),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -110,9 +110,9 @@ export class ApiKey extends Disposable {
|
|||||||
|
|
||||||
private _showRemoveKeyModal(): void {
|
private _showRemoveKeyModal(): void {
|
||||||
confirmModal(
|
confirmModal(
|
||||||
t('RemoveAPIKey'), t('Remove'),
|
t("Remove API Key"), t("Remove"),
|
||||||
() => this._onDelete(),
|
() => this._onDelete(),
|
||||||
t("AboutToDeleteAPIKey")
|
t("You're about to delete an API key. This will cause all future requests using this API key to be rejected. Do you still want to delete?")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,8 @@ export class App extends DisposableWithEvents {
|
|||||||
dom('table.g-help-table',
|
dom('table.g-help-table',
|
||||||
dom('thead',
|
dom('thead',
|
||||||
dom('tr',
|
dom('tr',
|
||||||
dom('th', t('Key')),
|
dom('th', t("Key")),
|
||||||
dom('th', t('Description'))
|
dom('th', t("Description"))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
dom.forEach(commandList.groups, (group: any) => {
|
dom.forEach(commandList.groups, (group: any) => {
|
||||||
@ -234,7 +234,7 @@ export class App extends DisposableWithEvents {
|
|||||||
if (message.match(/MemoryError|unmarshallable object/)) {
|
if (message.match(/MemoryError|unmarshallable object/)) {
|
||||||
if (err.message.length > 30) {
|
if (err.message.length > 30) {
|
||||||
// TLDR
|
// TLDR
|
||||||
err.message = t('MemoryError');
|
err.message = t("Memory Error");
|
||||||
}
|
}
|
||||||
this._mostRecentDocPageModel?.offerRecovery(err);
|
this._mostRecentDocPageModel?.offerRecovery(err);
|
||||||
}
|
}
|
||||||
|
@ -57,11 +57,11 @@ export class AppHeader extends Disposable {
|
|||||||
this._orgName && cssDropdownIcon('Dropdown'),
|
this._orgName && cssDropdownIcon('Dropdown'),
|
||||||
menu(() => [
|
menu(() => [
|
||||||
menuSubHeader(
|
menuSubHeader(
|
||||||
this._appModel.isTeamSite ? t('TeamSite') : t('PersonalSite')
|
this._appModel.isTeamSite ? t("Team Site") : t("Personal Site")
|
||||||
+ (this._appModel.isLegacySite ? ` (${t('Legacy')})` : ''),
|
+ (this._appModel.isLegacySite ? ` (${t("Legacy")})` : ''),
|
||||||
testId('orgmenu-title'),
|
testId('orgmenu-title'),
|
||||||
),
|
),
|
||||||
menuItemLink(urlState().setLinkUrl({}), t('HomePage'), testId('orgmenu-home-page')),
|
menuItemLink(urlState().setLinkUrl({}), t("Home Page"), testId('orgmenu-home-page')),
|
||||||
|
|
||||||
// Show 'Organization Settings' when on a home page of a valid org.
|
// Show 'Organization Settings' when on a home page of a valid org.
|
||||||
(!this._docPageModel && currentOrg && !currentOrg.owner ?
|
(!this._docPageModel && currentOrg && !currentOrg.owner ?
|
||||||
|
@ -20,14 +20,14 @@ export function CellContextMenu(rowOptions: IRowContextMenu, colOptions: IMultiC
|
|||||||
|
|
||||||
const numCols: number = colOptions.numColumns;
|
const numCols: number = colOptions.numColumns;
|
||||||
const nameClearColumns = colOptions.isFiltered ?
|
const nameClearColumns = colOptions.isFiltered ?
|
||||||
t("ResetEntireColumns", {count: numCols}) :
|
t("Reset {{count}} entire columns", {count: numCols}) :
|
||||||
t("ResetColumns", {count: numCols});
|
t("Reset {{count}} columns", {count: numCols});
|
||||||
const nameDeleteColumns = t("DeleteColumns", {count: numCols});
|
const nameDeleteColumns = t("Delete {{count}} columns", {count: numCols});
|
||||||
|
|
||||||
const numRows: number = rowOptions.numRows;
|
const numRows: number = rowOptions.numRows;
|
||||||
const nameDeleteRows = t("DeleteRows", {count: numRows});
|
const nameDeleteRows = t("Delete {{count}} rows", {count: numRows});
|
||||||
|
|
||||||
const nameClearCells = (numRows > 1 || numCols > 1) ? t('ClearValues') : t('ClearCell');
|
const nameClearCells = (numRows > 1 || numCols > 1) ? t("Clear values") : t("Clear cell");
|
||||||
|
|
||||||
const result: Array<Element|null> = [];
|
const result: Array<Element|null> = [];
|
||||||
|
|
||||||
@ -43,9 +43,9 @@ export function CellContextMenu(rowOptions: IRowContextMenu, colOptions: IMultiC
|
|||||||
...(
|
...(
|
||||||
(numCols > 1 || numRows > 1) ? [] : [
|
(numCols > 1 || numRows > 1) ? [] : [
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuItemCmd(allCommands.copyLink, t('CopyAnchorLink')),
|
menuItemCmd(allCommands.copyLink, t("Copy anchor link")),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuItemCmd(allCommands.filterByThisCellValue, t("FilterByValue")),
|
menuItemCmd(allCommands.filterByThisCellValue, t("Filter by this value")),
|
||||||
menuItemCmd(allCommands.openDiscussion, 'Comment', dom.cls('disabled', (
|
menuItemCmd(allCommands.openDiscussion, 'Comment', dom.cls('disabled', (
|
||||||
isReadonly || numRows === 0 || numCols === 0
|
isReadonly || numRows === 0 || numCols === 0
|
||||||
)), dom.hide(use => !use(COMMENTS()))) //TODO: i18next
|
)), dom.hide(use => !use(COMMENTS()))) //TODO: i18next
|
||||||
@ -60,19 +60,19 @@ export function CellContextMenu(rowOptions: IRowContextMenu, colOptions: IMultiC
|
|||||||
// When the view is sorted, any newly added records get shifts instantly at the top or
|
// When the view is sorted, any newly added records get shifts instantly at the top or
|
||||||
// bottom. It could be very confusing for users who might expect the record to stay above or
|
// bottom. It could be very confusing for users who might expect the record to stay above or
|
||||||
// below the active row. Thus in this case we show a single `insert row` command.
|
// below the active row. Thus in this case we show a single `insert row` command.
|
||||||
[menuItemCmd(allCommands.insertRecordAfter, t("InsertRow"),
|
[menuItemCmd(allCommands.insertRecordAfter, t("Insert row"),
|
||||||
dom.cls('disabled', disableInsert))] :
|
dom.cls('disabled', disableInsert))] :
|
||||||
|
|
||||||
[menuItemCmd(allCommands.insertRecordBefore, t("InsertRowAbove"),
|
[menuItemCmd(allCommands.insertRecordBefore, t("Insert row above"),
|
||||||
dom.cls('disabled', disableInsert)),
|
dom.cls('disabled', disableInsert)),
|
||||||
menuItemCmd(allCommands.insertRecordAfter, t("InsertRowBelow"),
|
menuItemCmd(allCommands.insertRecordAfter, t("Insert row below"),
|
||||||
dom.cls('disabled', disableInsert))]
|
dom.cls('disabled', disableInsert))]
|
||||||
),
|
),
|
||||||
menuItemCmd(allCommands.duplicateRows, t("DuplicateRows", {count: numRows}),
|
menuItemCmd(allCommands.duplicateRows, t("Duplicate rows", {count: numRows}),
|
||||||
dom.cls('disabled', disableInsert || numRows === 0)),
|
dom.cls('disabled', disableInsert || numRows === 0)),
|
||||||
menuItemCmd(allCommands.insertFieldBefore, t("InsertColumnLeft"),
|
menuItemCmd(allCommands.insertFieldBefore, t("Insert column to the left"),
|
||||||
disableForReadonlyView),
|
disableForReadonlyView),
|
||||||
menuItemCmd(allCommands.insertFieldAfter, t("InsertColumnRight"),
|
menuItemCmd(allCommands.insertFieldAfter, t("Insert column to the right"),
|
||||||
disableForReadonlyView),
|
disableForReadonlyView),
|
||||||
|
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio
|
|||||||
rangeInput(
|
rangeInput(
|
||||||
columnFilter.min, {
|
columnFilter.min, {
|
||||||
isDateFilter,
|
isDateFilter,
|
||||||
placeholder: isDateFilter ? t('DateRangeMin') : t('RangeMin'),
|
placeholder: isDateFilter ? t("Start") : t("Min"),
|
||||||
valueParser,
|
valueParser,
|
||||||
valueFormatter,
|
valueFormatter,
|
||||||
isSelected: isMinSelected,
|
isSelected: isMinSelected,
|
||||||
@ -154,7 +154,7 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio
|
|||||||
rangeInput(
|
rangeInput(
|
||||||
columnFilter.max, {
|
columnFilter.max, {
|
||||||
isDateFilter,
|
isDateFilter,
|
||||||
placeholder: isDateFilter ? t('DateRangeMax') : t('RangeMax'),
|
placeholder: isDateFilter ? t("End") : t("Max"),
|
||||||
valueParser,
|
valueParser,
|
||||||
valueFormatter,
|
valueFormatter,
|
||||||
isSelected: isMaxSelected,
|
isSelected: isMaxSelected,
|
||||||
@ -216,7 +216,7 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio
|
|||||||
searchInput = cssSearch(
|
searchInput = cssSearch(
|
||||||
searchValueObs, { onInput: true },
|
searchValueObs, { onInput: true },
|
||||||
testId('search-input'),
|
testId('search-input'),
|
||||||
{ type: 'search', placeholder: t('SearchValues') },
|
{ type: 'search', placeholder: t('Search values') },
|
||||||
dom.onKeyDown({
|
dom.onKeyDown({
|
||||||
Enter: () => {
|
Enter: () => {
|
||||||
if (searchValueObs.get()) {
|
if (searchValueObs.get()) {
|
||||||
@ -252,14 +252,14 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio
|
|||||||
const state = use(columnFilter.state);
|
const state = use(columnFilter.state);
|
||||||
return [
|
return [
|
||||||
cssSelectAll(
|
cssSelectAll(
|
||||||
dom.text(searchValue ? t('AllShown') : t('All')),
|
dom.text(searchValue ? t('All Shown') : t('All')),
|
||||||
dom.prop('disabled', isEquivalentFilter(state, allSpec)),
|
dom.prop('disabled', isEquivalentFilter(state, allSpec)),
|
||||||
dom.on('click', () => columnFilter.setState(allSpec)),
|
dom.on('click', () => columnFilter.setState(allSpec)),
|
||||||
testId('bulk-action'),
|
testId('bulk-action'),
|
||||||
),
|
),
|
||||||
cssDotSeparator('•'),
|
cssDotSeparator('•'),
|
||||||
cssSelectAll(
|
cssSelectAll(
|
||||||
searchValue ? t('AllExcept') : t('None'),
|
searchValue ? t('All Except') : t('None'),
|
||||||
dom.prop('disabled', isEquivalentFilter(state, noneSpec)),
|
dom.prop('disabled', isEquivalentFilter(state, noneSpec)),
|
||||||
dom.on('click', () => columnFilter.setState(noneSpec)),
|
dom.on('click', () => columnFilter.setState(noneSpec)),
|
||||||
testId('bulk-action'),
|
testId('bulk-action'),
|
||||||
@ -274,7 +274,7 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio
|
|||||||
),
|
),
|
||||||
cssItemList(
|
cssItemList(
|
||||||
testId('list'),
|
testId('list'),
|
||||||
dom.maybe(use => use(filteredValues).length === 0, () => cssNoResults(t('NoMatchingValues'))),
|
dom.maybe(use => use(filteredValues).length === 0, () => cssNoResults(t("No matching values"))),
|
||||||
dom.domComputed(filteredValues, (values) => values.slice(0, model.limitShown).map(([key, value]) => (
|
dom.domComputed(filteredValues, (values) => values.slice(0, model.limitShown).map(([key, value]) => (
|
||||||
cssMenuItem(
|
cssMenuItem(
|
||||||
cssLabel(
|
cssLabel(
|
||||||
@ -310,17 +310,17 @@ export function columnFilterMenu(owner: IDisposableOwner, opts: IFilterMenuOptio
|
|||||||
}
|
}
|
||||||
if (isAboveLimit) {
|
if (isAboveLimit) {
|
||||||
return searchValue ? [
|
return searchValue ? [
|
||||||
buildSummary(t('OtherMatching'), valuesBeyondLimit, false, model),
|
buildSummary(t("Other Matching"), valuesBeyondLimit, false, model),
|
||||||
buildSummary(t('OtherNonMatching'), otherValues, true, model),
|
buildSummary(t("Other Non-Matching"), otherValues, true, model),
|
||||||
] : [
|
] : [
|
||||||
buildSummary(t('OtherValues'), concat(otherValues, valuesBeyondLimit), false, model),
|
buildSummary(t("Other Values"), concat(otherValues, valuesBeyondLimit), false, model),
|
||||||
buildSummary(t('FutureValues'), [], true, model),
|
buildSummary(t("Future Values"), [], true, model),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return anyOtherValues ? [
|
return anyOtherValues ? [
|
||||||
buildSummary(t('Others'), otherValues, true, model)
|
buildSummary(t('Others'), otherValues, true, model)
|
||||||
] : [
|
] : [
|
||||||
buildSummary(t('FutureValues'), [], true, model)
|
buildSummary(t("Future Values"), [], true, model)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -61,7 +61,7 @@ class ColumnPicker extends Disposable {
|
|||||||
return [
|
return [
|
||||||
cssLabel(
|
cssLabel(
|
||||||
this._column.title,
|
this._column.title,
|
||||||
this._column.optional ? cssSubLabel(t('Optional')) : null,
|
this._column.optional ? cssSubLabel(t(" (optional)")) : null,
|
||||||
testId('label-for-' + this._column.name),
|
testId('label-for-' + this._column.name),
|
||||||
),
|
),
|
||||||
this._column.description ? cssHelp(
|
this._column.description ? cssHelp(
|
||||||
@ -73,7 +73,7 @@ class ColumnPicker extends Disposable {
|
|||||||
properValue,
|
properValue,
|
||||||
options,
|
options,
|
||||||
{
|
{
|
||||||
defaultLabel: this._column.typeDesc != "any" ? t('PickAColumnWithType', {"columnType": this._column.typeDesc}) : t('PickAColumn')
|
defaultLabel: this._column.typeDesc != "any" ? t("Pick a {{columnType}} column", {"columnType": this._column.typeDesc}) : t("Pick a column")
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
testId('mapping-for-' + this._column.name),
|
testId('mapping-for-' + this._column.name),
|
||||||
@ -105,7 +105,7 @@ class ColumnListPicker extends Disposable {
|
|||||||
return [
|
return [
|
||||||
cssRow(
|
cssRow(
|
||||||
cssAddMapping(
|
cssAddMapping(
|
||||||
cssAddIcon('Plus'), t('Add') + ' ' + this._column.title,
|
cssAddIcon('Plus'), t("Add") + ' ' + this._column.title,
|
||||||
menu(() => {
|
menu(() => {
|
||||||
const otherColumns = this._getNotMappedColumns();
|
const otherColumns = this._getNotMappedColumns();
|
||||||
const typedColumns = otherColumns.filter(this._typeFilter());
|
const typedColumns = otherColumns.filter(this._typeFilter());
|
||||||
@ -117,7 +117,7 @@ class ColumnListPicker extends Disposable {
|
|||||||
col.label.peek(),
|
col.label.peek(),
|
||||||
)),
|
)),
|
||||||
wrongTypeCount > 0 ? menuText(
|
wrongTypeCount > 0 ? menuText(
|
||||||
t("WrongTypesMenuText", {wrongTypeCount, columnType: this._column.type.toLowerCase(), count: wrongTypeCount}),
|
t("{{wrongTypeCount}} non-{{columnType}} columns are not shown", {wrongTypeCount, columnType: this._column.type.toLowerCase(), count: wrongTypeCount}),
|
||||||
testId('map-message-' + this._column.name)
|
testId('map-message-' + this._column.name)
|
||||||
) : null
|
) : null
|
||||||
];
|
];
|
||||||
@ -370,17 +370,17 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
switch(level) {
|
switch(level) {
|
||||||
case AccessLevel.none: return cssConfirmLine(t("WidgetNoPermissison"));
|
case AccessLevel.none: return cssConfirmLine(t("Widget does not require any permissions."));
|
||||||
case AccessLevel.read_table: return cssConfirmLine(t("WidgetNeedRead", {read: dom("b", "read")})); // TODO i18next
|
case AccessLevel.read_table: return cssConfirmLine(t("Widget needs to {{read}} the current table.", {read: dom("b", "read")})); // TODO i18next
|
||||||
case AccessLevel.full: return cssConfirmLine(t("WidgetNeedFullAccess", {fullAccess: dom("b", "full access")})); // TODO i18next
|
case AccessLevel.full: return cssConfirmLine(t("Widget needs {{fullAccess}} to this document.", {fullAccess: dom("b", "full access")})); // TODO i18next
|
||||||
default: throw new Error(`Unsupported ${level} access level`);
|
default: throw new Error(`Unsupported ${level} access level`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Options for access level.
|
// Options for access level.
|
||||||
const levels: IOptionFull<string>[] = [
|
const levels: IOptionFull<string>[] = [
|
||||||
{label: t('NoDocumentAccess'), value: AccessLevel.none},
|
{label: t("No document access"), value: AccessLevel.none},
|
||||||
{label: t('ReadSelectedTable'), value: AccessLevel.read_table},
|
{label: t("Read selected table"), value: AccessLevel.read_table},
|
||||||
{label: t('FullDocumentAccess'), value: AccessLevel.full},
|
{label: t("Full document access"), value: AccessLevel.full},
|
||||||
];
|
];
|
||||||
return dom(
|
return dom(
|
||||||
'div',
|
'div',
|
||||||
@ -388,7 +388,7 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
this._canSelect
|
this._canSelect
|
||||||
? cssRow(
|
? cssRow(
|
||||||
select(this._selectedId, options, {
|
select(this._selectedId, options, {
|
||||||
defaultLabel: t('SelectCustomWidget'),
|
defaultLabel: t("Select Custom Widget"),
|
||||||
menuCssClass: cssMenu.className,
|
menuCssClass: cssMenu.className,
|
||||||
}),
|
}),
|
||||||
testId('select')
|
testId('select')
|
||||||
@ -399,7 +399,7 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
cssTextInput(
|
cssTextInput(
|
||||||
this._url,
|
this._url,
|
||||||
async value => this._url.set(value),
|
async value => this._url.set(value),
|
||||||
dom.attr('placeholder', t('EnterCustomURL')),
|
dom.attr('placeholder', t("Enter Custom URL")),
|
||||||
testId('url')
|
testId('url')
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -440,7 +440,7 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
dom.maybe(this._hasConfiguration, () =>
|
dom.maybe(this._hasConfiguration, () =>
|
||||||
cssSection(
|
cssSection(
|
||||||
textButton(
|
textButton(
|
||||||
t('OpenConfiguration'),
|
t("Open configuration"),
|
||||||
dom.on('click', () => this._openConfiguration()),
|
dom.on('click', () => this._openConfiguration()),
|
||||||
testId('open-configuration')
|
testId('open-configuration')
|
||||||
)
|
)
|
||||||
@ -450,7 +450,7 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
cssLink(
|
cssLink(
|
||||||
dom.attr('href', 'https://support.getgrist.com/widget-custom'),
|
dom.attr('href', 'https://support.getgrist.com/widget-custom'),
|
||||||
dom.attr('target', '_blank'),
|
dom.attr('target', '_blank'),
|
||||||
t('LearnMore')
|
t("Learn more about custom widgets")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
dom.maybeOwned(use => use(this._section.columnsToMap), (owner, columns) => {
|
dom.maybeOwned(use => use(this._section.columnsToMap), (owner, columns) => {
|
||||||
|
@ -28,8 +28,8 @@ export class DocHistory extends Disposable implements IDomComponent {
|
|||||||
|
|
||||||
public buildDom() {
|
public buildDom() {
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{value: 'activity', label: t('Activity')},
|
{value: 'activity', label: t("Activity")},
|
||||||
{value: 'snapshots', label: t('Snapshots')},
|
{value: 'snapshots', label: t("Snapshots")},
|
||||||
];
|
];
|
||||||
return [
|
return [
|
||||||
cssSubTabs(
|
cssSubTabs(
|
||||||
@ -79,7 +79,7 @@ export class DocHistory extends Disposable implements IDomComponent {
|
|||||||
return dom(
|
return dom(
|
||||||
'div',
|
'div',
|
||||||
dom.maybe(snapshotsDenied, () => cssSnapshotDenied(
|
dom.maybe(snapshotsDenied, () => cssSnapshotDenied(
|
||||||
t('SnapshotsUnavailable'),
|
t("Snapshots are unavailable."),
|
||||||
testId('doc-history-error'))),
|
testId('doc-history-error'))),
|
||||||
// Note that most recent snapshots are first.
|
// Note that most recent snapshots are first.
|
||||||
dom.domComputed(snapshots, (snapshotList) => snapshotList.map((snapshot, index) => {
|
dom.domComputed(snapshots, (snapshotList) => snapshotList.map((snapshot, index) => {
|
||||||
@ -98,11 +98,11 @@ export class DocHistory extends Disposable implements IDomComponent {
|
|||||||
),
|
),
|
||||||
cssMenuDots(icon('Dots'),
|
cssMenuDots(icon('Dots'),
|
||||||
menu(() => [
|
menu(() => [
|
||||||
menuItemLink(setLink(snapshot), t('OpenSnapshot')),
|
menuItemLink(setLink(snapshot), t("Open Snapshot")),
|
||||||
menuItemLink(setLink(snapshot, origUrlId), t('CompareToCurrent'),
|
menuItemLink(setLink(snapshot, origUrlId), t("Compare to Current"),
|
||||||
menuAnnotate(t('Beta'))),
|
menuAnnotate(t("Beta"))),
|
||||||
prevSnapshot && menuItemLink(setLink(prevSnapshot, snapshot.docId), t('CompareToPrevious'),
|
prevSnapshot && menuItemLink(setLink(prevSnapshot, snapshot.docId), t("Compare to Previous"),
|
||||||
menuAnnotate(t('Beta'))),
|
menuAnnotate(t("Beta"))),
|
||||||
],
|
],
|
||||||
{placement: 'bottom-end', parentSelectorToMark: '.' + cssSnapshotCard.className}
|
{placement: 'bottom-end', parentSelectorToMark: '.' + cssSnapshotCard.className}
|
||||||
),
|
),
|
||||||
|
@ -71,8 +71,8 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
return css.docList(
|
return css.docList(
|
||||||
css.docMenu(
|
css.docMenu(
|
||||||
dom.maybe(!home.app.currentFeatures.workspaces, () => [
|
dom.maybe(!home.app.currentFeatures.workspaces, () => [
|
||||||
css.docListHeader(t('ServiceNotAvailable')),
|
css.docListHeader(t("This service is not available right now")),
|
||||||
dom('span', t('NeedPaidPlan')),
|
dom('span', t("(The organization needs a paid plan)")),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
// currentWS and showIntro observables change together. We capture both in one domComputed call.
|
// currentWS and showIntro observables change together. We capture both in one domComputed call.
|
||||||
@ -98,7 +98,7 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
// TODO: this is shown on all pages, but there is a hack in currentWSPinnedDocs that
|
// TODO: this is shown on all pages, but there is a hack in currentWSPinnedDocs that
|
||||||
// removes all pinned docs when on trash page.
|
// removes all pinned docs when on trash page.
|
||||||
dom.maybe((use) => use(home.currentWSPinnedDocs).length > 0, () => [
|
dom.maybe((use) => use(home.currentWSPinnedDocs).length > 0, () => [
|
||||||
css.docListHeader(css.pinnedDocsIcon('PinBig'), t('PinnedDocuments')),
|
css.docListHeader(css.pinnedDocsIcon('PinBig'), t("Pinned Documents")),
|
||||||
createPinnedDocs(home, home.currentWSPinnedDocs),
|
createPinnedDocs(home, home.currentWSPinnedDocs),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
dom.maybe((use) => page === 'templates' && use(home.featuredTemplates).length > 0, () => [
|
dom.maybe((use) => page === 'templates' && use(home.featuredTemplates).length > 0, () => [
|
||||||
css.featuredTemplatesHeader(
|
css.featuredTemplatesHeader(
|
||||||
css.featuredTemplatesIcon('Idea'),
|
css.featuredTemplatesIcon('Idea'),
|
||||||
t('Featured'),
|
t("Featured"),
|
||||||
testId('featured-templates-header')
|
testId('featured-templates-header')
|
||||||
),
|
),
|
||||||
createPinnedDocs(home, home.featuredTemplates, true),
|
createPinnedDocs(home, home.featuredTemplates, true),
|
||||||
@ -118,12 +118,12 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
null :
|
null :
|
||||||
css.docListHeader(
|
css.docListHeader(
|
||||||
(
|
(
|
||||||
page === 'all' ? t('AllDocuments') :
|
page === 'all' ? t("All Documents") :
|
||||||
page === 'templates' ?
|
page === 'templates' ?
|
||||||
dom.domComputed(use => use(home.featuredTemplates).length > 0, (hasFeaturedTemplates) =>
|
dom.domComputed(use => use(home.featuredTemplates).length > 0, (hasFeaturedTemplates) =>
|
||||||
hasFeaturedTemplates ? t('MoreExamplesAndTemplates') : t('ExamplesAndTemplates')
|
hasFeaturedTemplates ? t("More Examples and Templates") : t("Examples and Templates")
|
||||||
) :
|
) :
|
||||||
page === 'trash' ? t('Trash') :
|
page === 'trash' ? t("Trash") :
|
||||||
workspace && [css.docHeaderIcon('Folder'), workspaceName(home.app, workspace)]
|
workspace && [css.docHeaderIcon('Folder'), workspaceName(home.app, workspace)]
|
||||||
),
|
),
|
||||||
testId('doc-header'),
|
testId('doc-header'),
|
||||||
@ -138,9 +138,9 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
) :
|
) :
|
||||||
(page === 'trash') ?
|
(page === 'trash') ?
|
||||||
dom('div',
|
dom('div',
|
||||||
css.docBlock(t('DocStayInTrash')),
|
css.docBlock(t("Documents stay in Trash for 30 days, after which they get deleted permanently.")),
|
||||||
dom.maybe((use) => use(home.trashWorkspaces).length === 0, () =>
|
dom.maybe((use) => use(home.trashWorkspaces).length === 0, () =>
|
||||||
css.docBlock(t("EmptyTrash"))
|
css.docBlock(t("Trash is empty."))
|
||||||
),
|
),
|
||||||
buildAllDocsBlock(home, home.trashWorkspaces, false, flashDocId, viewSettings),
|
buildAllDocsBlock(home, home.trashWorkspaces, false, flashDocId, viewSettings),
|
||||||
) :
|
) :
|
||||||
@ -155,7 +155,7 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
) :
|
) :
|
||||||
workspace && !workspace.isSupportWorkspace && workspace.docs?.length === 0 ?
|
workspace && !workspace.isSupportWorkspace && workspace.docs?.length === 0 ?
|
||||||
buildWorkspaceIntro(home) :
|
buildWorkspaceIntro(home) :
|
||||||
css.docBlock(t('WorkspaceNotFound'))
|
css.docBlock(t("Workspace not found"))
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
@ -187,7 +187,7 @@ function buildAllDocsBlock(
|
|||||||
|
|
||||||
(ws.removedAt ?
|
(ws.removedAt ?
|
||||||
[
|
[
|
||||||
css.docRowUpdatedAt(t('Deleted', {at:getTimeFromNow(ws.removedAt)})),
|
css.docRowUpdatedAt(t("Deleted {{at}}", {at:getTimeFromNow(ws.removedAt)})),
|
||||||
css.docMenuTrigger(icon('Dots')),
|
css.docMenuTrigger(icon('Dots')),
|
||||||
menu(() => makeRemovedWsOptionsMenu(home, ws),
|
menu(() => makeRemovedWsOptionsMenu(home, ws),
|
||||||
{placement: 'bottom-end', parentSelectorToMark: '.' + css.docRowWrapper.className}),
|
{placement: 'bottom-end', parentSelectorToMark: '.' + css.docRowWrapper.className}),
|
||||||
@ -221,7 +221,7 @@ function buildAllDocsTemplates(home: HomeModel, viewSettings: ViewSettings) {
|
|||||||
dom.autoDispose(hideTemplatesObs),
|
dom.autoDispose(hideTemplatesObs),
|
||||||
css.templatesHeaderWrap(
|
css.templatesHeaderWrap(
|
||||||
css.templatesHeader(
|
css.templatesHeader(
|
||||||
t('Examples&Templates'),
|
t("Examples & Templates"),
|
||||||
dom.domComputed(hideTemplatesObs, (collapsed) =>
|
dom.domComputed(hideTemplatesObs, (collapsed) =>
|
||||||
collapsed ? css.templatesHeaderIcon('Expand') : css.templatesHeaderIcon('Collapse')
|
collapsed ? css.templatesHeaderIcon('Expand') : css.templatesHeaderIcon('Collapse')
|
||||||
),
|
),
|
||||||
@ -233,7 +233,7 @@ function buildAllDocsTemplates(home: HomeModel, viewSettings: ViewSettings) {
|
|||||||
dom.maybe((use) => !use(hideTemplatesObs), () => [
|
dom.maybe((use) => !use(hideTemplatesObs), () => [
|
||||||
buildTemplateDocs(home, templates, viewSettings),
|
buildTemplateDocs(home, templates, viewSettings),
|
||||||
bigBasicButton(
|
bigBasicButton(
|
||||||
t('DiscoverMoreTemplates'),
|
t("Discover More Templates"),
|
||||||
urlState().setLinkUrl({homePage: 'templates'}),
|
urlState().setLinkUrl({homePage: 'templates'}),
|
||||||
testId('all-docs-templates-discover-more'),
|
testId('all-docs-templates-discover-more'),
|
||||||
)
|
)
|
||||||
@ -281,7 +281,7 @@ function buildOtherSites(home: HomeModel) {
|
|||||||
return css.otherSitesBlock(
|
return css.otherSitesBlock(
|
||||||
dom.autoDispose(hideOtherSitesObs),
|
dom.autoDispose(hideOtherSitesObs),
|
||||||
css.otherSitesHeader(
|
css.otherSitesHeader(
|
||||||
t('OtherSites'),
|
t("Other Sites"),
|
||||||
dom.domComputed(hideOtherSitesObs, (collapsed) =>
|
dom.domComputed(hideOtherSitesObs, (collapsed) =>
|
||||||
collapsed ? css.otherSitesHeaderIcon('Expand') : css.otherSitesHeaderIcon('Collapse')
|
collapsed ? css.otherSitesHeaderIcon('Expand') : css.otherSitesHeaderIcon('Collapse')
|
||||||
),
|
),
|
||||||
@ -293,7 +293,8 @@ function buildOtherSites(home: HomeModel) {
|
|||||||
const siteName = home.app.currentOrgName;
|
const siteName = home.app.currentOrgName;
|
||||||
return [
|
return [
|
||||||
dom('div',
|
dom('div',
|
||||||
t('OtherSitesWelcome', { siteName, context: personal ? 'personal' : '' }),
|
personal ? t("You are on your personal site. You also have access to the following sites:") :
|
||||||
|
t("You are on the {{siteName}} site. You also have access to the following sites:", {siteName}),
|
||||||
testId('other-sites-message')
|
testId('other-sites-message')
|
||||||
),
|
),
|
||||||
css.otherSitesButtons(
|
css.otherSitesButtons(
|
||||||
@ -305,7 +306,7 @@ function buildOtherSites(home: HomeModel) {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
testId('other-sites-buttons')
|
testId('other-sites-buttons')
|
||||||
)
|
),
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -329,8 +330,8 @@ function buildPrefs(
|
|||||||
// The Sort selector.
|
// The Sort selector.
|
||||||
options.hideSort ? null : dom.update(
|
options.hideSort ? null : dom.update(
|
||||||
select<SortPref>(viewSettings.currentSort, [
|
select<SortPref>(viewSettings.currentSort, [
|
||||||
{value: 'name', label: t('ByName')},
|
{value: 'name', label: t("By Name")},
|
||||||
{value: 'date', label: t('ByDateModified')},
|
{value: 'date', label: t("By Date Modified")},
|
||||||
],
|
],
|
||||||
{ buttonCssClass: css.sortSelector.className },
|
{ buttonCssClass: css.sortSelector.className },
|
||||||
),
|
),
|
||||||
@ -386,8 +387,8 @@ function buildWorkspaceDocBlock(home: HomeModel, workspace: Workspace, flashDocI
|
|||||||
),
|
),
|
||||||
css.docRowUpdatedAt(
|
css.docRowUpdatedAt(
|
||||||
(doc.removedAt ?
|
(doc.removedAt ?
|
||||||
t('Deleted', {at: getTimeFromNow(doc.removedAt)}) :
|
t("Deleted {{at}}", {at: getTimeFromNow(doc.removedAt)}) :
|
||||||
t('Edited', {at: getTimeFromNow(doc.updatedAt)})),
|
t("Edited {{at}}", {at: getTimeFromNow(doc.updatedAt)})),
|
||||||
testId('doc-time')
|
testId('doc-time')
|
||||||
),
|
),
|
||||||
(doc.removedAt ?
|
(doc.removedAt ?
|
||||||
@ -421,7 +422,7 @@ function buildWorkspaceDocBlock(home: HomeModel, workspace: Workspace, flashDocI
|
|||||||
save: (val) => doRename(home, doc, val, flashDocId),
|
save: (val) => doRename(home, doc, val, flashDocId),
|
||||||
close: () => renaming.set(null),
|
close: () => renaming.set(null),
|
||||||
}, testId('doc-name-editor')),
|
}, testId('doc-name-editor')),
|
||||||
css.docRowUpdatedAt(t('Edited', {at: getTimeFromNow(doc.updatedAt)}), testId('doc-time')),
|
css.docRowUpdatedAt(t("Edited {{at}}", {at: getTimeFromNow(doc.updatedAt)}), testId('doc-time')),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
testId('doc')
|
testId('doc')
|
||||||
@ -462,9 +463,9 @@ export function makeDocOptionsMenu(home: HomeModel, doc: Document, renaming: Obs
|
|||||||
const orgAccess: roles.Role|null = org ? org.access : null;
|
const orgAccess: roles.Role|null = org ? org.access : null;
|
||||||
|
|
||||||
function deleteDoc() {
|
function deleteDoc() {
|
||||||
confirmModal(t('DeleteDoc', {name: doc.name}), t('Delete'),
|
confirmModal(t("Delete {{name}}", {name: doc.name}), t("Delete"),
|
||||||
() => home.deleteDoc(doc.id, false).catch(reportError),
|
() => home.deleteDoc(doc.id, false).catch(reportError),
|
||||||
t('DocumentMoveToTrash'));
|
t("Document will be moved to Trash."));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function manageUsers() {
|
async function manageUsers() {
|
||||||
@ -487,7 +488,7 @@ export function makeDocOptionsMenu(home: HomeModel, doc: Document, renaming: Obs
|
|||||||
dom.cls('disabled', !roles.canEdit(doc.access)),
|
dom.cls('disabled', !roles.canEdit(doc.access)),
|
||||||
testId('rename-doc')
|
testId('rename-doc')
|
||||||
),
|
),
|
||||||
menuItem(() => showMoveDocModal(home, doc), t('Move'),
|
menuItem(() => showMoveDocModal(home, doc), t("Move"),
|
||||||
// Note that moving the doc requires ACL access on the doc. Moving a doc to a workspace
|
// Note that moving the doc requires ACL access on the doc. Moving a doc to a workspace
|
||||||
// that confers descendant ACL access could otherwise increase the user's access to the doc.
|
// that confers descendant ACL access could otherwise increase the user's access to the doc.
|
||||||
// By requiring the user to have ACL edit access on the doc to move it prevents using this
|
// By requiring the user to have ACL edit access on the doc to move it prevents using this
|
||||||
@ -498,16 +499,16 @@ export function makeDocOptionsMenu(home: HomeModel, doc: Document, renaming: Obs
|
|||||||
dom.cls('disabled', !roles.canEditAccess(doc.access)),
|
dom.cls('disabled', !roles.canEditAccess(doc.access)),
|
||||||
testId('move-doc')
|
testId('move-doc')
|
||||||
),
|
),
|
||||||
menuItem(deleteDoc, t('Remove'),
|
menuItem(deleteDoc, t("Remove"),
|
||||||
dom.cls('disabled', !roles.isOwner(doc)),
|
dom.cls('disabled', !roles.isOwner(doc)),
|
||||||
testId('delete-doc')
|
testId('delete-doc')
|
||||||
),
|
),
|
||||||
menuItem(() => home.pinUnpinDoc(doc.id, !doc.isPinned).catch(reportError),
|
menuItem(() => home.pinUnpinDoc(doc.id, !doc.isPinned).catch(reportError),
|
||||||
doc.isPinned ? t("UnpinDocument"): t("PinDocument"),
|
doc.isPinned ? t("Unpin Document"): t("Pin Document"),
|
||||||
dom.cls('disabled', !roles.canEdit(orgAccess)),
|
dom.cls('disabled', !roles.canEdit(orgAccess)),
|
||||||
testId('pin-doc')
|
testId('pin-doc')
|
||||||
),
|
),
|
||||||
menuItem(manageUsers, roles.canEditAccess(doc.access) ? t("ManageUsers"): t("AccessDetails"),
|
menuItem(manageUsers, roles.canEditAccess(doc.access) ? t("Manage Users"): t("Access Details"),
|
||||||
testId('doc-access')
|
testId('doc-access')
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
@ -515,22 +516,22 @@ export function makeDocOptionsMenu(home: HomeModel, doc: Document, renaming: Obs
|
|||||||
|
|
||||||
export function makeRemovedDocOptionsMenu(home: HomeModel, doc: Document, workspace: Workspace) {
|
export function makeRemovedDocOptionsMenu(home: HomeModel, doc: Document, workspace: Workspace) {
|
||||||
function hardDeleteDoc() {
|
function hardDeleteDoc() {
|
||||||
confirmModal(t("DeleteForeverDoc", {name: doc.name}), t("DeleteForever"),
|
confirmModal(t("Permanently Delete \"{{name}}\"?", {name: doc.name}), t("Delete Forever"),
|
||||||
() => home.deleteDoc(doc.id, true).catch(reportError),
|
() => home.deleteDoc(doc.id, true).catch(reportError),
|
||||||
t('DeleteDocPerma'));
|
t("Document will be permanently deleted."));
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
menuItem(() => home.restoreDoc(doc), t('Restore'),
|
menuItem(() => home.restoreDoc(doc), t("Restore"),
|
||||||
dom.cls('disabled', !roles.isOwner(doc) || !!workspace.removedAt),
|
dom.cls('disabled', !roles.isOwner(doc) || !!workspace.removedAt),
|
||||||
testId('doc-restore')
|
testId('doc-restore')
|
||||||
),
|
),
|
||||||
menuItem(hardDeleteDoc, t('DeleteForever'),
|
menuItem(hardDeleteDoc, t("Delete Forever"),
|
||||||
dom.cls('disabled', !roles.isOwner(doc)),
|
dom.cls('disabled', !roles.isOwner(doc)),
|
||||||
testId('doc-delete-forever')
|
testId('doc-delete-forever')
|
||||||
),
|
),
|
||||||
(workspace.removedAt ?
|
(workspace.removedAt ?
|
||||||
menuText(t('RestoreThisDocument')) :
|
menuText(t("To restore this document, restore the workspace first.")) :
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
@ -538,16 +539,16 @@ export function makeRemovedDocOptionsMenu(home: HomeModel, doc: Document, worksp
|
|||||||
|
|
||||||
function makeRemovedWsOptionsMenu(home: HomeModel, ws: Workspace) {
|
function makeRemovedWsOptionsMenu(home: HomeModel, ws: Workspace) {
|
||||||
return [
|
return [
|
||||||
menuItem(() => home.restoreWorkspace(ws), t('Restore'),
|
menuItem(() => home.restoreWorkspace(ws), t("Restore"),
|
||||||
dom.cls('disabled', !roles.canDelete(ws.access)),
|
dom.cls('disabled', !roles.canDelete(ws.access)),
|
||||||
testId('ws-restore')
|
testId('ws-restore')
|
||||||
),
|
),
|
||||||
menuItem(() => home.deleteWorkspace(ws.id, true), t('DeleteForever'),
|
menuItem(() => home.deleteWorkspace(ws.id, true), t("Delete Forever"),
|
||||||
dom.cls('disabled', !roles.canDelete(ws.access) || ws.docs.length > 0),
|
dom.cls('disabled', !roles.canDelete(ws.access) || ws.docs.length > 0),
|
||||||
testId('ws-delete-forever')
|
testId('ws-delete-forever')
|
||||||
),
|
),
|
||||||
(ws.docs.length > 0 ?
|
(ws.docs.length > 0 ?
|
||||||
menuText(t('DeleteWorkspaceForever')) :
|
menuText(t("You may delete a workspace forever once it has no documents in it.")) :
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
@ -565,8 +566,8 @@ function showMoveDocModal(home: HomeModel, doc: Document) {
|
|||||||
const disabled = isCurrent || !isEditable;
|
const disabled = isCurrent || !isEditable;
|
||||||
return css.moveDocListItem(
|
return css.moveDocListItem(
|
||||||
css.moveDocListText(workspaceName(home.app, ws)),
|
css.moveDocListText(workspaceName(home.app, ws)),
|
||||||
isCurrent ? css.moveDocListHintText(t('CurrentWorkspace')) : null,
|
isCurrent ? css.moveDocListHintText(t("Current workspace")) : null,
|
||||||
!isEditable ? css.moveDocListHintText(t('RequiresEditPermissions')) : null,
|
!isEditable ? css.moveDocListHintText(t("Requires edit permissions")) : null,
|
||||||
css.moveDocListItem.cls('-disabled', disabled),
|
css.moveDocListItem.cls('-disabled', disabled),
|
||||||
css.moveDocListItem.cls('-selected', (use) => use(selected) === ws.id),
|
css.moveDocListItem.cls('-selected', (use) => use(selected) === ws.id),
|
||||||
dom.on('click', () => disabled || selected.set(ws.id)),
|
dom.on('click', () => disabled || selected.set(ws.id)),
|
||||||
@ -576,11 +577,11 @@ function showMoveDocModal(home: HomeModel, doc: Document) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
title: t('MoveDocToWorkspace', {name: doc.name}),
|
title: t("Move {{name}} to workspace", {name: doc.name}),
|
||||||
body,
|
body,
|
||||||
saveDisabled: Computed.create(owner, (use) => !use(selected)),
|
saveDisabled: Computed.create(owner, (use) => !use(selected)),
|
||||||
saveFunc: async () => !selected.get() || home.moveDoc(doc.id, selected.get()!).catch(reportError),
|
saveFunc: async () => !selected.get() || home.moveDoc(doc.id, selected.get()!).catch(reportError),
|
||||||
saveLabel: t('Move'),
|
saveLabel: t("Move"),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ export async function startDocTour(docData: DocData, docComm: DocComm, onFinishC
|
|||||||
}
|
}
|
||||||
|
|
||||||
const invalidDocTour: IOnBoardingMsg[] = [{
|
const invalidDocTour: IOnBoardingMsg[] = [{
|
||||||
title: t('InvalidDocTourTitle'),
|
title: t("No valid document tour"),
|
||||||
body: t('InvalidDocTourBody'),
|
body: t("Cannot construct a document tour from the data in this document. Ensure there is a table named GristDocTour with columns Title, Body, Placement, and Location."),
|
||||||
selector: 'document',
|
selector: 'document',
|
||||||
showHasModal: true,
|
showHasModal: true,
|
||||||
}];
|
}];
|
||||||
|
@ -42,23 +42,23 @@ export async function showDocSettingsModal(docInfo: DocInfoRec, docPageModel: Do
|
|||||||
const canChangeEngine = getSupportedEngineChoices().length > 0;
|
const canChangeEngine = getSupportedEngineChoices().length > 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: t('DocumentSettings'),
|
title: t("Document Settings"),
|
||||||
body: [
|
body: [
|
||||||
cssDataRow(t('ThisDocumentID')),
|
cssDataRow(t("This document's ID (for API use):")),
|
||||||
cssDataRow(dom('tt', docPageModel.currentDocId.get())),
|
cssDataRow(dom('tt', docPageModel.currentDocId.get())),
|
||||||
cssDataRow(t('TimeZone')),
|
cssDataRow(t("Time Zone:")),
|
||||||
cssDataRow(dom.create(buildTZAutocomplete, moment, timezoneObs, (val) => timezoneObs.set(val))),
|
cssDataRow(dom.create(buildTZAutocomplete, moment, timezoneObs, (val) => timezoneObs.set(val))),
|
||||||
cssDataRow(t('Locale')),
|
cssDataRow(t("Locale:")),
|
||||||
cssDataRow(dom.create(buildLocaleSelect, localeObs)),
|
cssDataRow(dom.create(buildLocaleSelect, localeObs)),
|
||||||
cssDataRow(t('Currency')),
|
cssDataRow(t("Currency:")),
|
||||||
cssDataRow(dom.domComputed(localeObs, (l) =>
|
cssDataRow(dom.domComputed(localeObs, (l) =>
|
||||||
dom.create(buildCurrencyPicker, currencyObs, (val) => currencyObs.set(val),
|
dom.create(buildCurrencyPicker, currencyObs, (val) => currencyObs.set(val),
|
||||||
{defaultCurrencyLabel: t('LocalCurrency', {currency: getCurrency(l)})})
|
{defaultCurrencyLabel: t("Local currency ({{currency}})", {currency: getCurrency(l)})})
|
||||||
)),
|
)),
|
||||||
canChangeEngine ? [
|
canChangeEngine ? [
|
||||||
// Small easter egg: you can click on the skull-and-crossbones to
|
// Small easter egg: you can click on the skull-and-crossbones to
|
||||||
// force a reload of the document.
|
// force a reload of the document.
|
||||||
cssDataRow(t('EngineRisk', {span:
|
cssDataRow(t("Engine (experimental {{span}} change at own risk):", {span:
|
||||||
dom('span', '☠',
|
dom('span', '☠',
|
||||||
dom.style('cursor', 'pointer'),
|
dom.style('cursor', 'pointer'),
|
||||||
dom.on('click', async () => {
|
dom.on('click', async () => {
|
||||||
@ -71,7 +71,7 @@ export async function showDocSettingsModal(docInfo: DocInfoRec, docPageModel: Do
|
|||||||
],
|
],
|
||||||
// Modal label is "Save", unless engine is changed. If engine is changed, the document will
|
// Modal label is "Save", unless engine is changed. If engine is changed, the document will
|
||||||
// need a reload to switch engines, so we replace the label with "Save and Reload".
|
// need a reload to switch engines, so we replace the label with "Save and Reload".
|
||||||
saveLabel: dom.text((use) => (use(engineObs) === docSettings.engine) ? t('Save') : t('SaveAndReload')),
|
saveLabel: dom.text((use) => (use(engineObs) === docSettings.engine) ? t("Save") : t("Save and Reload")),
|
||||||
saveFunc: async () => {
|
saveFunc: async () => {
|
||||||
await docInfo.updateColValues({
|
await docInfo.updateColValues({
|
||||||
timezone: timezoneObs.get(),
|
timezone: timezoneObs.get(),
|
||||||
|
@ -74,7 +74,7 @@ class DuplicateTableModal extends Disposable {
|
|||||||
input(
|
input(
|
||||||
this._newTableName,
|
this._newTableName,
|
||||||
{onInput: true},
|
{onInput: true},
|
||||||
{placeholder: t('NewName')},
|
{placeholder: t("Name for new table")},
|
||||||
(elem) => { setTimeout(() => { elem.focus(); }, 20); },
|
(elem) => { setTimeout(() => { elem.focus(); }, 20); },
|
||||||
dom.on('focus', (_ev, elem) => { elem.select(); }),
|
dom.on('focus', (_ev, elem) => { elem.select(); }),
|
||||||
dom.cls(cssInput.className),
|
dom.cls(cssInput.className),
|
||||||
@ -85,19 +85,19 @@ class DuplicateTableModal extends Disposable {
|
|||||||
cssWarningIcon('Warning'),
|
cssWarningIcon('Warning'),
|
||||||
|
|
||||||
dom('div',
|
dom('div',
|
||||||
t("AdviceWithLink", {link: cssLink({href: commonUrls.helpLinkingWidgets, target: '_blank'}, 'Read More.')})
|
t("Instead of duplicating tables, it's usually better to segment data using linked views. {{link}}", {link: cssLink({href: commonUrls.helpLinkingWidgets, target: '_blank'}, 'Read More.')})
|
||||||
), //TODO: i18next
|
), //TODO: i18next
|
||||||
),
|
),
|
||||||
cssField(
|
cssField(
|
||||||
cssCheckbox(
|
cssCheckbox(
|
||||||
this._includeData,
|
this._includeData,
|
||||||
t('CopyAllData'),
|
t("Copy all data in addition to the table structure."),
|
||||||
testId('copy-all-data'),
|
testId('copy-all-data'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
dom.maybe(this._includeData, () => cssWarning(
|
dom.maybe(this._includeData, () => cssWarning(
|
||||||
cssWarningIcon('Warning'),
|
cssWarningIcon('Warning'),
|
||||||
dom('div', t('WarningACL')),
|
dom('div', t("Only the document default access rules will apply to the copy.")),
|
||||||
testId('acl-warning'),
|
testId('acl-warning'),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
|
@ -20,34 +20,34 @@ interface WelcomeCard {
|
|||||||
export const buildExamples = (): IExampleInfo[] => [{
|
export const buildExamples = (): IExampleInfo[] => [{
|
||||||
id: 1, // Identifies the example in UserPrefs.seenExamples
|
id: 1, // Identifies the example in UserPrefs.seenExamples
|
||||||
urlId: 'lightweight-crm',
|
urlId: 'lightweight-crm',
|
||||||
title: t('Title', {context: "CRM"}),
|
title: t('Lightweight CRM'),
|
||||||
imgUrl: 'https://www.getgrist.com/themes/grist/assets/images/use-cases/lightweight-crm.png',
|
imgUrl: 'https://www.getgrist.com/themes/grist/assets/images/use-cases/lightweight-crm.png',
|
||||||
tutorialUrl: 'https://support.getgrist.com/lightweight-crm/',
|
tutorialUrl: 'https://support.getgrist.com/lightweight-crm/',
|
||||||
welcomeCard: {
|
welcomeCard: {
|
||||||
title: t('WelcomeTitle', {context: "CRM"}),
|
title: t('Welcome to the Lightweight CRM template'),
|
||||||
text: t('WelcomeText', {context: "CRM"}),
|
text: t('Check out our related tutorial for how to link data, and create high-productivity layouts.'),
|
||||||
tutorialName: t('WelcomeTutorialName', {context: "CRM"}),
|
tutorialName: t("Tutorial: Create a CRM"),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 2, // Identifies the example in UserPrefs.seenExamples
|
id: 2, // Identifies the example in UserPrefs.seenExamples
|
||||||
urlId: 'investment-research',
|
urlId: 'investment-research',
|
||||||
title: t('Title', {context: "investmentResearch"}),
|
title: t('Investment Research'),
|
||||||
imgUrl: 'https://www.getgrist.com/themes/grist/assets/images/use-cases/data-visualization.png',
|
imgUrl: 'https://www.getgrist.com/themes/grist/assets/images/use-cases/data-visualization.png',
|
||||||
tutorialUrl: 'https://support.getgrist.com/investment-research/',
|
tutorialUrl: 'https://support.getgrist.com/investment-research/',
|
||||||
welcomeCard: {
|
welcomeCard: {
|
||||||
title: t('WelcomeTitle', {context: "investmentResearch"}),
|
title: t("Welcome to the Investment Research template"),
|
||||||
text: t('WelcomeText', {context: "investmentResearch"}),
|
text: t("Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically."),
|
||||||
tutorialName: t('WelcomeTutorialName', {context: "investmentResearch"}),
|
tutorialName: t("Tutorial: Analyze & Visualize"),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 3, // Identifies the example in UserPrefs.seenExamples
|
id: 3, // Identifies the example in UserPrefs.seenExamples
|
||||||
urlId: 'afterschool-program',
|
urlId: 'afterschool-program',
|
||||||
title: t('Title', {context: "afterschool"}),
|
title: t('Afterschool Program'),
|
||||||
imgUrl: 'https://www.getgrist.com/themes/grist/assets/images/use-cases/business-management.png',
|
imgUrl: 'https://www.getgrist.com/themes/grist/assets/images/use-cases/business-management.png',
|
||||||
tutorialUrl: 'https://support.getgrist.com/afterschool-program/',
|
tutorialUrl: 'https://support.getgrist.com/afterschool-program/',
|
||||||
welcomeCard: {
|
welcomeCard: {
|
||||||
title: t('WelcomeTitle', {context: "afterschool"}),
|
title: t("Welcome to the Afterschool Program template"),
|
||||||
text: t('WelcomeText', {context: "afterschool"}),
|
text: t("Check out our related tutorial for how to model business data, use formulas, and manage complexity."),
|
||||||
tutorialName: t('WelcomeTutorialName', {context: "afterschool"}),
|
tutorialName: t("Tutorial: Manage Business Data"),
|
||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
|
@ -54,7 +54,7 @@ export function buildNameConfig(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
cssLabel(t('ColumnLabel')),
|
cssLabel(t("COLUMN LABEL AND ID")),
|
||||||
cssRow(
|
cssRow(
|
||||||
dom.cls(cssBlockedCursor.className, origColumn.disableModify),
|
dom.cls(cssBlockedCursor.className, origColumn.disableModify),
|
||||||
cssColLabelBlock(
|
cssColLabelBlock(
|
||||||
@ -84,7 +84,7 @@ export function buildNameConfig(
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
dom.maybe(isSummaryTable,
|
dom.maybe(isSummaryTable,
|
||||||
() => cssRow(t('ColumnOptionsLimited')))
|
() => cssRow(t("Column options are limited in summary tables.")))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,19 +210,19 @@ export function buildFormulaConfig(
|
|||||||
const behaviorName = Computed.create(owner, behavior, (use, type) => {
|
const behaviorName = Computed.create(owner, behavior, (use, type) => {
|
||||||
if (use(isMultiSelect)) {
|
if (use(isMultiSelect)) {
|
||||||
const commonType = use(multiType);
|
const commonType = use(multiType);
|
||||||
if (commonType === 'formula') { return t('ColumnType', {context: 'formula', count: 2}); }
|
if (commonType === 'formula') { return t('Formula Columns', {count: 2}); }
|
||||||
if (commonType === 'data') { return t('ColumnType', {context: 'data', count: 2}); }
|
if (commonType === 'data') { return t('Data Columns', {count: 2}); }
|
||||||
if (commonType === 'mixed') { return t('ColumnType', {context: 'mixed', count: 2}); }
|
if (commonType === 'mixed') { return t('Mixed Behavior'); }
|
||||||
return t('ColumnType', {context: 'empty', count: 2});
|
return t('Empty Columns', {count: 2});
|
||||||
} else {
|
} else {
|
||||||
if (type === 'formula') { return t('ColumnType', {context: 'formula', count: 1}); }
|
if (type === 'formula') { return t('Formula Columns', {count: 1}); }
|
||||||
if (type === 'data') { return t('ColumnType', {context: 'data', count: 1}); }
|
if (type === 'data') { return t('Data Columns', {count: 1}); }
|
||||||
return t('ColumnType', {context: 'empty', count: 1});
|
return t('Empty Columns', {count: 1});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const behaviorIcon = Computed.create<IconName>(owner, (use) => {
|
const behaviorIcon = Computed.create<IconName>(owner, (use) => {
|
||||||
return use(behaviorName) === t('ColumnType', {context: 'data', count: 2}) ||
|
return use(behaviorName) === t('Data Columns', {count: 2}) ||
|
||||||
use(behaviorName) === t('ColumnType', {context: 'data', count: 1}) ? "Database" : "Script";
|
use(behaviorName) === t('Data Columns', {count: 1}) ? "Database" : "Script";
|
||||||
});
|
});
|
||||||
const behaviorLabel = () => selectTitle(behaviorName, behaviorIcon);
|
const behaviorLabel = () => selectTitle(behaviorName, behaviorIcon);
|
||||||
|
|
||||||
@ -231,26 +231,26 @@ export function buildFormulaConfig(
|
|||||||
// Converts data column to formula column.
|
// Converts data column to formula column.
|
||||||
const convertDataColumnToFormulaOption = () => selectOption(
|
const convertDataColumnToFormulaOption = () => selectOption(
|
||||||
() => (maybeFormula.set(true), formulaField?.focus()),
|
() => (maybeFormula.set(true), formulaField?.focus()),
|
||||||
t('ConvertColumn', {context: 'formula'}), 'Script');
|
t("Clear and make into formula"), 'Script');
|
||||||
|
|
||||||
// Converts to empty column and opens up the editor. (label is the same, but this is used when we have no formula)
|
// Converts to empty column and opens up the editor. (label is the same, but this is used when we have no formula)
|
||||||
const convertTriggerToFormulaOption = () => selectOption(
|
const convertTriggerToFormulaOption = () => selectOption(
|
||||||
() => gristDoc.convertIsFormula([origColumn.id.peek()], {toFormula: true, noRecalc: true}),
|
() => gristDoc.convertIsFormula([origColumn.id.peek()], {toFormula: true, noRecalc: true}),
|
||||||
t('ConvertColumn', {context: 'formula'}), 'Script');
|
t("Clear and make into formula"), 'Script');
|
||||||
|
|
||||||
// Convert column to data.
|
// Convert column to data.
|
||||||
// This method is also available through a text button.
|
// This method is also available through a text button.
|
||||||
const convertToData = () => gristDoc.convertIsFormula([origColumn.id.peek()], {toFormula: false, noRecalc: true});
|
const convertToData = () => gristDoc.convertIsFormula([origColumn.id.peek()], {toFormula: false, noRecalc: true});
|
||||||
const convertToDataOption = () => selectOption(
|
const convertToDataOption = () => selectOption(
|
||||||
convertToData,
|
convertToData,
|
||||||
t('ConvertColumn', {context: 'data'}), 'Database',
|
t("Convert column to data"), 'Database',
|
||||||
dom.cls('disabled', isSummaryTable)
|
dom.cls('disabled', isSummaryTable)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Clears the column
|
// Clears the column
|
||||||
const clearAndResetOption = () => selectOption(
|
const clearAndResetOption = () => selectOption(
|
||||||
() => gristDoc.clearColumns([origColumn.id.peek()]),
|
() => gristDoc.clearColumns([origColumn.id.peek()]),
|
||||||
t('ClearAndReset'), 'CrossSmall');
|
t("Clear and reset"), 'CrossSmall');
|
||||||
|
|
||||||
// Actions on text buttons:
|
// Actions on text buttons:
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ export function buildFormulaConfig(
|
|||||||
cssRow(formulaField = buildFormula(
|
cssRow(formulaField = buildFormula(
|
||||||
origColumn,
|
origColumn,
|
||||||
buildEditor,
|
buildEditor,
|
||||||
t('EnterFormula'),
|
t("Enter formula"),
|
||||||
disableOtherActions,
|
disableOtherActions,
|
||||||
onSave,
|
onSave,
|
||||||
clearState)),
|
clearState)),
|
||||||
@ -322,21 +322,21 @@ export function buildFormulaConfig(
|
|||||||
];
|
];
|
||||||
|
|
||||||
return dom.maybe(behavior, (type: BEHAVIOR) => [
|
return dom.maybe(behavior, (type: BEHAVIOR) => [
|
||||||
cssLabel(t('ColumnBehavior')),
|
cssLabel(t("COLUMN BEHAVIOR")),
|
||||||
...(type === "empty" ? [
|
...(type === "empty" ? [
|
||||||
menu(behaviorLabel(), [
|
menu(behaviorLabel(), [
|
||||||
convertToDataOption(),
|
convertToDataOption(),
|
||||||
]),
|
]),
|
||||||
cssEmptySeparator(),
|
cssEmptySeparator(),
|
||||||
cssRow(textButton(
|
cssRow(textButton(
|
||||||
t('SetFormula'),
|
t("Set formula"),
|
||||||
dom.on("click", setFormula),
|
dom.on("click", setFormula),
|
||||||
dom.prop("disabled", disableOtherActions),
|
dom.prop("disabled", disableOtherActions),
|
||||||
testId("field-set-formula")
|
testId("field-set-formula")
|
||||||
)),
|
)),
|
||||||
cssRow(withInfoTooltip(
|
cssRow(withInfoTooltip(
|
||||||
textButton(
|
textButton(
|
||||||
t('SetTriggerFormula'),
|
t("Set trigger formula"),
|
||||||
dom.on("click", setTrigger),
|
dom.on("click", setTrigger),
|
||||||
dom.prop("disabled", use => use(isSummaryTable) || use(disableOtherActions)),
|
dom.prop("disabled", use => use(isSummaryTable) || use(disableOtherActions)),
|
||||||
testId("field-set-trigger")
|
testId("field-set-trigger")
|
||||||
@ -344,7 +344,7 @@ export function buildFormulaConfig(
|
|||||||
GristTooltips.setTriggerFormula(),
|
GristTooltips.setTriggerFormula(),
|
||||||
)),
|
)),
|
||||||
cssRow(textButton(
|
cssRow(textButton(
|
||||||
t('MakeIntoDataColumn'),
|
t("Make into data column"),
|
||||||
dom.on("click", convertToData),
|
dom.on("click", convertToData),
|
||||||
dom.prop("disabled", use => use(isSummaryTable) || use(disableOtherActions)),
|
dom.prop("disabled", use => use(isSummaryTable) || use(disableOtherActions)),
|
||||||
testId("field-set-data")
|
testId("field-set-data")
|
||||||
@ -357,7 +357,7 @@ export function buildFormulaConfig(
|
|||||||
formulaBuilder(onSaveConvertToFormula),
|
formulaBuilder(onSaveConvertToFormula),
|
||||||
cssEmptySeparator(),
|
cssEmptySeparator(),
|
||||||
cssRow(textButton(
|
cssRow(textButton(
|
||||||
t('ConvertColumn', {context: 'triggerformula'}),
|
t("Convert to trigger formula"),
|
||||||
dom.on("click", convertFormulaToTrigger),
|
dom.on("click", convertFormulaToTrigger),
|
||||||
dom.hide(maybeFormula),
|
dom.hide(maybeFormula),
|
||||||
dom.prop("disabled", use => use(isSummaryTable) || use(disableOtherActions)),
|
dom.prop("disabled", use => use(isSummaryTable) || use(disableOtherActions)),
|
||||||
@ -377,7 +377,7 @@ export function buildFormulaConfig(
|
|||||||
),
|
),
|
||||||
// If data column is or wants to be a trigger formula:
|
// If data column is or wants to be a trigger formula:
|
||||||
dom.maybe((use) => use(maybeTrigger) || use(origColumn.hasTriggerFormula), () => [
|
dom.maybe((use) => use(maybeTrigger) || use(origColumn.hasTriggerFormula), () => [
|
||||||
cssLabel(t('TriggerFormula')),
|
cssLabel(t("TRIGGER FORMULA")),
|
||||||
formulaBuilder(onSaveConvertToTrigger),
|
formulaBuilder(onSaveConvertToTrigger),
|
||||||
dom.create(buildFormulaTriggers, origColumn, {
|
dom.create(buildFormulaTriggers, origColumn, {
|
||||||
disabled: disableOtherActions,
|
disabled: disableOtherActions,
|
||||||
@ -389,7 +389,7 @@ export function buildFormulaConfig(
|
|||||||
cssEmptySeparator(),
|
cssEmptySeparator(),
|
||||||
cssRow(withInfoTooltip(
|
cssRow(withInfoTooltip(
|
||||||
textButton(
|
textButton(
|
||||||
t("SetTriggerFormula"),
|
t("Set trigger formula"),
|
||||||
dom.on("click", convertDataColumnToTriggerColumn),
|
dom.on("click", convertDataColumnToTriggerColumn),
|
||||||
dom.prop("disabled", disableOtherActions),
|
dom.prop("disabled", disableOtherActions),
|
||||||
testId("field-set-trigger")
|
testId("field-set-trigger")
|
||||||
|
@ -13,10 +13,10 @@ const t = makeT('FieldMenus');
|
|||||||
export function FieldSettingsMenu(useColOptions: boolean, disableSeparate: boolean, actions: IFieldOptions) {
|
export function FieldSettingsMenu(useColOptions: boolean, disableSeparate: boolean, actions: IFieldOptions) {
|
||||||
useColOptions = useColOptions || disableSeparate;
|
useColOptions = useColOptions || disableSeparate;
|
||||||
return [
|
return [
|
||||||
menuSubHeader(t('UsingSettings', {context: useColOptions ? 'common' : 'separate'})),
|
menuSubHeader(useColOptions ? t("Using common settings") : t("Using separate settings")),
|
||||||
useColOptions ? menuItem(actions.useSeparate, t('Settings', {context: 'useseparate'}), dom.cls('disabled', disableSeparate)) : [
|
useColOptions ? menuItem(actions.useSeparate, t("Use separate settings"), dom.cls('disabled', disableSeparate)) : [
|
||||||
menuItem(actions.saveAsCommon, t('Settings', {context: 'savecommon'})),
|
menuItem(actions.saveAsCommon, t("Save as common settings")),
|
||||||
menuItem(actions.revertToCommon, t('Settings', {context: 'revertcommon'})),
|
menuItem(actions.revertToCommon, t("Revert to common settings")),
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ export class FilterConfig extends Disposable {
|
|||||||
dom.domComputed((use) => {
|
dom.domComputed((use) => {
|
||||||
const filters = use(this._section.filters);
|
const filters = use(this._section.filters);
|
||||||
return cssTextBtn(
|
return cssTextBtn(
|
||||||
t('AddColumn'),
|
t("Add Column"),
|
||||||
addFilterMenu(filters, this._popupControls, {
|
addFilterMenu(filters, this._popupControls, {
|
||||||
menuOptions: {
|
menuOptions: {
|
||||||
placement: 'bottom-end',
|
placement: 'bottom-end',
|
||||||
|
@ -20,23 +20,23 @@ export class GridOptions extends Disposable {
|
|||||||
public buildDom() {
|
public buildDom() {
|
||||||
const section = this._section;
|
const section = this._section;
|
||||||
return [
|
return [
|
||||||
cssLabel(t('GridOptions')),
|
cssLabel(t("Grid Options")),
|
||||||
dom('div', [
|
dom('div', [
|
||||||
cssRow(
|
cssRow(
|
||||||
checkbox(setSaveValueFromKo(this, section.optionsObj.prop('verticalGridlines'))),
|
checkbox(setSaveValueFromKo(this, section.optionsObj.prop('verticalGridlines'))),
|
||||||
t('VerticalGridlines'),
|
t("Vertical Gridlines"),
|
||||||
testId('v-grid-button')
|
testId('v-grid-button')
|
||||||
),
|
),
|
||||||
|
|
||||||
cssRow(
|
cssRow(
|
||||||
checkbox(setSaveValueFromKo(this, section.optionsObj.prop('horizontalGridlines'))),
|
checkbox(setSaveValueFromKo(this, section.optionsObj.prop('horizontalGridlines'))),
|
||||||
t('HorizontalGridlines'),
|
t("Horizontal Gridlines"),
|
||||||
testId('h-grid-button')
|
testId('h-grid-button')
|
||||||
),
|
),
|
||||||
|
|
||||||
cssRow(
|
cssRow(
|
||||||
checkbox(setSaveValueFromKo(this, section.optionsObj.prop('zebraStripes'))),
|
checkbox(setSaveValueFromKo(this, section.optionsObj.prop('zebraStripes'))),
|
||||||
t('ZebraStripes'),
|
t("Zebra Stripes"),
|
||||||
testId('zebra-stripe-button')
|
testId('zebra-stripe-button')
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -26,13 +26,13 @@ interface IViewSection {
|
|||||||
*/
|
*/
|
||||||
export function ColumnAddMenu(gridView: IView, viewSection: IViewSection) {
|
export function ColumnAddMenu(gridView: IView, viewSection: IViewSection) {
|
||||||
return [
|
return [
|
||||||
menuItem(() => gridView.addNewColumn(), t('AddColumn')),
|
menuItem(() => gridView.addNewColumn(), t("Add Column")),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
...viewSection.hiddenColumns().map((col: any) => menuItem(
|
...viewSection.hiddenColumns().map((col: any) => menuItem(
|
||||||
() => {
|
() => {
|
||||||
gridView.showColumn(col.id(), viewSection.viewFields().peekLength);
|
gridView.showColumn(col.id(), viewSection.viewFields().peekLength);
|
||||||
// .then(() => gridView.scrollPaneRight());
|
// .then(() => gridView.scrollPaneRight());
|
||||||
}, t('ShowColumn', {label: col.label()})))
|
}, t("Show column {{- label}}", {label: col.label()})))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
export interface IMultiColumnContextMenu {
|
export interface IMultiColumnContextMenu {
|
||||||
@ -68,13 +68,13 @@ export function ColumnContextMenu(options: IColumnContextMenu) {
|
|||||||
const addToSortLabel = getAddToSortLabel(sortSpec, colId);
|
const addToSortLabel = getAddToSortLabel(sortSpec, colId);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
menuItemCmd(allCommands.fieldTabOpen, t('ColumnOptions')),
|
menuItemCmd(allCommands.fieldTabOpen, t("Column Options")),
|
||||||
menuItem(filterOpenFunc, t('FilterData')),
|
menuItem(filterOpenFunc, t("Filter Data")),
|
||||||
menuDivider({style: 'margin-bottom: 0;'}),
|
menuDivider({style: 'margin-bottom: 0;'}),
|
||||||
cssRowMenuItem(
|
cssRowMenuItem(
|
||||||
customMenuItem(
|
customMenuItem(
|
||||||
allCommands.sortAsc.run,
|
allCommands.sortAsc.run,
|
||||||
dom('span', t('Sort'), {style: 'flex: 1 0 auto; margin-right: 8px;'},
|
dom('span', t("Sort"), {style: 'flex: 1 0 auto; margin-right: 8px;'},
|
||||||
testId('sort-label')),
|
testId('sort-label')),
|
||||||
icon('Sort', dom.style('transform', 'scaley(-1)')),
|
icon('Sort', dom.style('transform', 'scaley(-1)')),
|
||||||
'A-Z',
|
'A-Z',
|
||||||
@ -112,9 +112,9 @@ export function ColumnContextMenu(options: IColumnContextMenu) {
|
|||||||
),
|
),
|
||||||
] : null,
|
] : null,
|
||||||
menuDivider({style: 'margin-bottom: 0; margin-top: 0;'}),
|
menuDivider({style: 'margin-bottom: 0; margin-top: 0;'}),
|
||||||
menuItem(allCommands.sortFilterTabOpen.run, t('MoreSortOptions'), testId('more-sort-options')),
|
menuItem(allCommands.sortFilterTabOpen.run, t("More sort options ..."), testId('more-sort-options')),
|
||||||
menuDivider({style: 'margin-top: 0;'}),
|
menuDivider({style: 'margin-top: 0;'}),
|
||||||
menuItemCmd(allCommands.renameField, t('RenameColumn'), disableForReadonlyColumn),
|
menuItemCmd(allCommands.renameField, t("Rename column"), disableForReadonlyColumn),
|
||||||
freezeMenuItemCmd(options),
|
freezeMenuItemCmd(options),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
MultiColumnMenu((options.disableFrozenMenu = true, options)),
|
MultiColumnMenu((options.disableFrozenMenu = true, options)),
|
||||||
@ -135,29 +135,29 @@ export function MultiColumnMenu(options: IMultiColumnContextMenu) {
|
|||||||
const disableForReadonlyView = dom.cls('disabled', options.isReadonly);
|
const disableForReadonlyView = dom.cls('disabled', options.isReadonly);
|
||||||
const num: number = options.numColumns;
|
const num: number = options.numColumns;
|
||||||
const nameClearColumns = options.isFiltered ?
|
const nameClearColumns = options.isFiltered ?
|
||||||
t('ResetEntireColumns', {count: num}) :
|
t('Reset {{count}} entire columns', {count: num}) :
|
||||||
t('ResetColumns', {count: num});
|
t('Reset {{count}} columns', {count: num});
|
||||||
const nameDeleteColumns = t('DeleteColumns', {count: num});
|
const nameDeleteColumns = t('Delete {{count}} columns', {count: num});
|
||||||
const nameHideColumns = t('HideColumns', {count: num});
|
const nameHideColumns = t('Hide {{count}} columns', {count: num});
|
||||||
const frozenMenu = options.disableFrozenMenu ? null : freezeMenuItemCmd(options);
|
const frozenMenu = options.disableFrozenMenu ? null : freezeMenuItemCmd(options);
|
||||||
return [
|
return [
|
||||||
frozenMenu ? [frozenMenu, menuDivider()]: null,
|
frozenMenu ? [frozenMenu, menuDivider()]: null,
|
||||||
// Offered only when selection includes formula columns, and converts only those.
|
// Offered only when selection includes formula columns, and converts only those.
|
||||||
(options.isFormula ?
|
(options.isFormula ?
|
||||||
menuItemCmd(allCommands.convertFormulasToData, t('ConvertFormulaToData'),
|
menuItemCmd(allCommands.convertFormulasToData, t("Convert formula to data"),
|
||||||
disableForReadonlyColumn) : null),
|
disableForReadonlyColumn) : null),
|
||||||
|
|
||||||
// With data columns selected, offer an additional option to clear out selected cells.
|
// With data columns selected, offer an additional option to clear out selected cells.
|
||||||
(options.isFormula !== true ?
|
(options.isFormula !== true ?
|
||||||
menuItemCmd(allCommands.clearValues, t('ClearValues'), disableForReadonlyColumn) : null),
|
menuItemCmd(allCommands.clearValues, t("Clear values"), disableForReadonlyColumn) : null),
|
||||||
|
|
||||||
(!options.isRaw ? menuItemCmd(allCommands.hideFields, nameHideColumns, disableForReadonlyView) : null),
|
(!options.isRaw ? menuItemCmd(allCommands.hideFields, nameHideColumns, disableForReadonlyView) : null),
|
||||||
menuItemCmd(allCommands.clearColumns, nameClearColumns, disableForReadonlyColumn),
|
menuItemCmd(allCommands.clearColumns, nameClearColumns, disableForReadonlyColumn),
|
||||||
menuItemCmd(allCommands.deleteFields, nameDeleteColumns, disableForReadonlyColumn),
|
menuItemCmd(allCommands.deleteFields, nameDeleteColumns, disableForReadonlyColumn),
|
||||||
|
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuItemCmd(allCommands.insertFieldBefore, t('InsertColumn', {to: 'left'}), disableForReadonlyView),
|
menuItemCmd(allCommands.insertFieldBefore, t("Insert column to the {{to}}", {to: 'left'}), disableForReadonlyView),
|
||||||
menuItemCmd(allCommands.insertFieldAfter, t('InsertColumn', {to: 'right'}), disableForReadonlyView)
|
menuItemCmd(allCommands.insertFieldAfter, t("Insert column to the {{to}}", {to: 'right'}), disableForReadonlyView)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,12 +206,14 @@ export function freezeAction(options: IMultiColumnContextMenu): { text: string;
|
|||||||
|
|
||||||
// if user clicked the first column or a column just after frozen set
|
// if user clicked the first column or a column just after frozen set
|
||||||
if (firstColumnIndex === 0 || firstColumnIndex === numFrozen) {
|
if (firstColumnIndex === 0 || firstColumnIndex === numFrozen) {
|
||||||
text = t('FreezeColumn', {count: 1});
|
text = t('Freeze {{count}} columns', {count: 1});
|
||||||
} else {
|
} else {
|
||||||
// else user clicked any other column that is farther, offer to freeze
|
// else user clicked any other column that is farther, offer to freeze
|
||||||
// proper number of column
|
// proper number of column
|
||||||
const properNumber = firstColumnIndex - numFrozen + 1;
|
const properNumber = firstColumnIndex - numFrozen + 1;
|
||||||
text = t('FreezeColumn', {count: properNumber, context: numFrozen ? 'more' : '' });
|
text = numFrozen ?
|
||||||
|
t('Freeze {{count}} more columns', {count: properNumber}) :
|
||||||
|
t('Freeze {{count}} columns', {count: properNumber});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
@ -220,12 +222,14 @@ export function freezeAction(options: IMultiColumnContextMenu): { text: string;
|
|||||||
} else if (isFrozenColumn) {
|
} else if (isFrozenColumn) {
|
||||||
// when user clicked last column in frozen set - offer to unfreeze this column
|
// when user clicked last column in frozen set - offer to unfreeze this column
|
||||||
if (firstColumnIndex + 1 === numFrozen) {
|
if (firstColumnIndex + 1 === numFrozen) {
|
||||||
text = t('UnfreezeColumn', {count: 1});
|
text = t('Unfreeze {{count}} columns', {count: 1});
|
||||||
} else {
|
} else {
|
||||||
// else user clicked column that is not the last in a frozen set
|
// else user clicked column that is not the last in a frozen set
|
||||||
// offer to unfreeze proper number of columns
|
// offer to unfreeze proper number of columns
|
||||||
const properNumber = numFrozen - firstColumnIndex;
|
const properNumber = numFrozen - firstColumnIndex;
|
||||||
text = t('UnfreezeColumn', {count: properNumber, context: properNumber === numFrozen ? 'all' : '' });
|
text = properNumber === numFrozen ?
|
||||||
|
t('Unfreeze all columns') :
|
||||||
|
t('Unfreeze {{count}} columns', {count: properNumber});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
@ -236,20 +240,20 @@ export function freezeAction(options: IMultiColumnContextMenu): { text: string;
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isLastFrozenSet) {
|
if (isLastFrozenSet) {
|
||||||
text = t('UnfreezeColumn', {count: length});
|
text = t('Unfreeze {{count}} columns', {count: length});
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
numFrozen : numFrozen - length
|
numFrozen : numFrozen - length
|
||||||
};
|
};
|
||||||
} else if (isFirstNormalSet) {
|
} else if (isFirstNormalSet) {
|
||||||
text = t('FreezeColumn', {count: length});
|
text = t('Freeze {{count}} columns', {count: length});
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
numFrozen : numFrozen + length
|
numFrozen : numFrozen + length
|
||||||
};
|
};
|
||||||
} else if (isSpanSet) {
|
} else if (isSpanSet) {
|
||||||
const toFreeze = lastColumnIndex + 1 - numFrozen;
|
const toFreeze = lastColumnIndex + 1 - numFrozen;
|
||||||
text = t('FreezeColumn', {count: toFreeze, context: 'more'});
|
text = t('Freeze {{count}} more columns', {count: toFreeze});
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
numFrozen : numFrozen + toFreeze
|
numFrozen : numFrozen + toFreeze
|
||||||
@ -278,9 +282,9 @@ function getAddToSortLabel(sortSpec: Sort.SortSpec, colId: number): string|undef
|
|||||||
if (sortSpec.length !== 0 && !isEqual(columnsInSpec, [colId])) {
|
if (sortSpec.length !== 0 && !isEqual(columnsInSpec, [colId])) {
|
||||||
const index = columnsInSpec.indexOf(colId);
|
const index = columnsInSpec.indexOf(colId);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
return t('AddToSort', {count: index + 1, context: 'added'});
|
return t("Sorted (#{{count}})", {count: index + 1});
|
||||||
} else {
|
} else {
|
||||||
return t('AddToSort');
|
return t("Add to sort");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export function buildHomeIntro(homeModel: HomeModel): DomContents {
|
|||||||
export function buildWorkspaceIntro(homeModel: HomeModel): DomContents {
|
export function buildWorkspaceIntro(homeModel: HomeModel): DomContents {
|
||||||
const isViewer = homeModel.currentWS.get()?.access === roles.VIEWER;
|
const isViewer = homeModel.currentWS.get()?.access === roles.VIEWER;
|
||||||
const isAnonym = !homeModel.app.currentValidUser;
|
const isAnonym = !homeModel.app.currentValidUser;
|
||||||
const emptyLine = cssIntroLine(testId('empty-workspace-info'), t('EmptyWorkspace'));
|
const emptyLine = cssIntroLine(testId('empty-workspace-info'), t("This workspace is empty."));
|
||||||
if (isAnonym || isViewer) {
|
if (isAnonym || isViewer) {
|
||||||
return emptyLine;
|
return emptyLine;
|
||||||
} else {
|
} else {
|
||||||
@ -58,38 +58,38 @@ function makeViewerTeamSiteIntro(homeModel: HomeModel) {
|
|||||||
const docLink = (dom.maybe(personalOrg, org => {
|
const docLink = (dom.maybe(personalOrg, org => {
|
||||||
return cssLink(
|
return cssLink(
|
||||||
urlState().setLinkUrl({org: org.domain ?? undefined}),
|
urlState().setLinkUrl({org: org.domain ?? undefined}),
|
||||||
t('PersonalSite'),
|
t("personal site"),
|
||||||
testId('welcome-personal-url'));
|
testId('welcome-personal-url'));
|
||||||
}));
|
}));
|
||||||
return [
|
return [
|
||||||
css.docListHeader(
|
css.docListHeader(
|
||||||
dom.autoDispose(personalOrg),
|
dom.autoDispose(personalOrg),
|
||||||
t('WelcomeTo', {orgName: homeModel.app.currentOrgName}),
|
t("Welcome to {{orgName}}", {orgName: homeModel.app.currentOrgName}),
|
||||||
productPill(homeModel.app.currentOrg, {large: true}),
|
productPill(homeModel.app.currentOrg, {large: true}),
|
||||||
testId('welcome-title')
|
testId('welcome-title')
|
||||||
),
|
),
|
||||||
cssIntroLine(
|
cssIntroLine(
|
||||||
testId('welcome-info'),
|
testId('welcome-info'),
|
||||||
t('WelcomeInfoNoDocuments'),
|
t("You have read-only access to this site. Currently there are no documents."),
|
||||||
dom('br'),
|
dom('br'),
|
||||||
t('WelcomeInfoAppearHere'),
|
t("Any documents created in this site will appear here."),
|
||||||
),
|
),
|
||||||
cssIntroLine(
|
cssIntroLine(
|
||||||
t('WelcomeTextVistGrist'), docLink, '.',
|
t("Interested in using Grist outside of your team? Visit your free "), docLink, '.',
|
||||||
testId('welcome-text')
|
testId('welcome-text')
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTeamSiteIntro(homeModel: HomeModel) {
|
function makeTeamSiteIntro(homeModel: HomeModel) {
|
||||||
const sproutsProgram = cssLink({href: commonUrls.sproutsProgram, target: '_blank'}, t('SproutsProgram'));
|
const sproutsProgram = cssLink({href: commonUrls.sproutsProgram, target: '_blank'}, t("Sprouts Program"));
|
||||||
return [
|
return [
|
||||||
css.docListHeader(
|
css.docListHeader(
|
||||||
t('WelcomeTo', {orgName: homeModel.app.currentOrgName}),
|
t("Welcome to {{orgName}}", {orgName: homeModel.app.currentOrgName}),
|
||||||
productPill(homeModel.app.currentOrg, {large: true}),
|
productPill(homeModel.app.currentOrg, {large: true}),
|
||||||
testId('welcome-title')
|
testId('welcome-title')
|
||||||
),
|
),
|
||||||
cssIntroLine(t('TeamSiteIntroGetStarted')),
|
cssIntroLine(t("Get started by inviting your team and creating your first Grist document.")),
|
||||||
(shouldHideUiElement('helpCenter') ? null :
|
(shouldHideUiElement('helpCenter') ? null :
|
||||||
cssIntroLine(
|
cssIntroLine(
|
||||||
'Learn more in our ', helpCenterLink(), ', or find an expert via our ', sproutsProgram, '.', // TODO i18n
|
'Learn more in our ', helpCenterLink(), ', or find an expert via our ', sproutsProgram, '.', // TODO i18n
|
||||||
@ -102,10 +102,10 @@ function makeTeamSiteIntro(homeModel: HomeModel) {
|
|||||||
|
|
||||||
function makePersonalIntro(homeModel: HomeModel, user: FullUser) {
|
function makePersonalIntro(homeModel: HomeModel, user: FullUser) {
|
||||||
return [
|
return [
|
||||||
css.docListHeader(t('WelcomeUser', {name: user.name}), testId('welcome-title')),
|
css.docListHeader(t("Welcome to Grist, {{name}}!", {name: user.name}), testId('welcome-title')),
|
||||||
cssIntroLine(t('PersonalIntroGetStarted')),
|
cssIntroLine(t("Get started by creating your first Grist document.")),
|
||||||
(shouldHideUiElement('helpCenter') ? null :
|
(shouldHideUiElement('helpCenter') ? null :
|
||||||
cssIntroLine(t('VisitHelpCenter', { link: helpCenterLink() }),
|
cssIntroLine(t("Visit our {{link}} to learn more.", { link: helpCenterLink() }),
|
||||||
testId('welcome-text'))
|
testId('welcome-text'))
|
||||||
),
|
),
|
||||||
makeCreateButtons(homeModel),
|
makeCreateButtons(homeModel),
|
||||||
@ -113,19 +113,19 @@ function makePersonalIntro(homeModel: HomeModel, user: FullUser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeAnonIntro(homeModel: HomeModel) {
|
function makeAnonIntro(homeModel: HomeModel) {
|
||||||
const signUp = cssLink({href: getLoginOrSignupUrl()}, t('SignUp'));
|
const signUp = cssLink({href: getLoginOrSignupUrl()}, t("Sign up"));
|
||||||
return [
|
return [
|
||||||
css.docListHeader(t('Welcome'), testId('welcome-title')),
|
css.docListHeader(t("Welcome to Grist!"), testId('welcome-title')),
|
||||||
cssIntroLine(t('AnonIntroGetStarted')),
|
cssIntroLine(t("Get started by exploring templates, or creating your first Grist document.")),
|
||||||
cssIntroLine(signUp, ' to save your work. ', // TODO i18n
|
cssIntroLine(signUp, ' to save your work. ', // TODO i18n
|
||||||
(shouldHideUiElement('helpCenter') ? null : t('VisitHelpCenter', { link: helpCenterLink() })),
|
(shouldHideUiElement('helpCenter') ? null : t("Visit our {{link}} to learn more.", { link: helpCenterLink() })),
|
||||||
testId('welcome-text')),
|
testId('welcome-text')),
|
||||||
makeCreateButtons(homeModel),
|
makeCreateButtons(homeModel),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function helpCenterLink() {
|
function helpCenterLink() {
|
||||||
return cssLink({href: commonUrls.help, target: '_blank'}, cssInlineIcon('Help'), t('HelpCenter'));
|
return cssLink({href: commonUrls.help, target: '_blank'}, cssInlineIcon('Help'), t("Help Center"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildButtons(homeModel: HomeModel, options: {
|
function buildButtons(homeModel: HomeModel, options: {
|
||||||
@ -136,22 +136,22 @@ function buildButtons(homeModel: HomeModel, options: {
|
|||||||
}) {
|
}) {
|
||||||
return cssBtnGroup(
|
return cssBtnGroup(
|
||||||
!options.invite ? null :
|
!options.invite ? null :
|
||||||
cssBtn(cssBtnIcon('Help'), t('InviteTeamMembers'), testId('intro-invite'),
|
cssBtn(cssBtnIcon('Help'), t("Invite Team Members"), testId('intro-invite'),
|
||||||
cssButton.cls('-primary'),
|
cssButton.cls('-primary'),
|
||||||
dom.on('click', () => manageTeamUsersApp(homeModel.app)),
|
dom.on('click', () => manageTeamUsersApp(homeModel.app)),
|
||||||
),
|
),
|
||||||
!options.templates ? null :
|
!options.templates ? null :
|
||||||
cssBtn(cssBtnIcon('FieldTable'), t('BrowseTemplates'), testId('intro-templates'),
|
cssBtn(cssBtnIcon('FieldTable'), t("Browse Templates"), testId('intro-templates'),
|
||||||
cssButton.cls('-primary'),
|
cssButton.cls('-primary'),
|
||||||
dom.hide(shouldHideUiElement("templates")),
|
dom.hide(shouldHideUiElement("templates")),
|
||||||
urlState().setLinkUrl({homePage: 'templates'}),
|
urlState().setLinkUrl({homePage: 'templates'}),
|
||||||
),
|
),
|
||||||
!options.import ? null :
|
!options.import ? null :
|
||||||
cssBtn(cssBtnIcon('Import'), t('ImportDocument'), testId('intro-import-doc'),
|
cssBtn(cssBtnIcon('Import'), t("Import Document"), testId('intro-import-doc'),
|
||||||
dom.on('click', () => importDocAndOpen(homeModel)),
|
dom.on('click', () => importDocAndOpen(homeModel)),
|
||||||
),
|
),
|
||||||
!options.empty ? null :
|
!options.empty ? null :
|
||||||
cssBtn(cssBtnIcon('Page'), t('CreateEmptyDocument'), testId('intro-create-doc'),
|
cssBtn(cssBtnIcon('Page'), t("Create Empty Document"), testId('intro-create-doc'),
|
||||||
dom.on('click', () => createDocAndOpen(homeModel)),
|
dom.on('click', () => createDocAndOpen(homeModel)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -42,14 +42,14 @@ export function createHomeLeftPane(leftPanelOpen: Observable<boolean>, home: Hom
|
|||||||
cssPageEntry(
|
cssPageEntry(
|
||||||
cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "all"),
|
cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "all"),
|
||||||
cssPageLink(cssPageIcon('Home'),
|
cssPageLink(cssPageIcon('Home'),
|
||||||
cssLinkText(t('AllDocuments')),
|
cssLinkText(t("All Documents")),
|
||||||
urlState().setLinkUrl({ws: undefined, homePage: undefined}),
|
urlState().setLinkUrl({ws: undefined, homePage: undefined}),
|
||||||
testId('dm-all-docs'),
|
testId('dm-all-docs'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
dom.maybe(use => !use(home.singleWorkspace), () =>
|
dom.maybe(use => !use(home.singleWorkspace), () =>
|
||||||
cssSectionHeader(
|
cssSectionHeader(
|
||||||
t('Workspaces'),
|
t("Workspaces"),
|
||||||
// Give it a testId, because it's a good element to simulate "click-away" in tests.
|
// Give it a testId, because it's a good element to simulate "click-away" in tests.
|
||||||
testId('dm-ws-label')
|
testId('dm-ws-label')
|
||||||
),
|
),
|
||||||
@ -108,7 +108,7 @@ export function createHomeLeftPane(leftPanelOpen: Observable<boolean>, home: Hom
|
|||||||
cssPageEntry(
|
cssPageEntry(
|
||||||
dom.hide(shouldHideUiElement("templates")),
|
dom.hide(shouldHideUiElement("templates")),
|
||||||
cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "templates"),
|
cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "templates"),
|
||||||
cssPageLink(cssPageIcon('FieldTable'), cssLinkText(t("ExamplesAndTemplates")),
|
cssPageLink(cssPageIcon('FieldTable'), cssLinkText(t("Examples & Templates")),
|
||||||
urlState().setLinkUrl({homePage: "templates"}),
|
urlState().setLinkUrl({homePage: "templates"}),
|
||||||
testId('dm-templates-page'),
|
testId('dm-templates-page'),
|
||||||
),
|
),
|
||||||
@ -176,11 +176,11 @@ function addMenu(home: HomeModel, creating: Observable<boolean>): DomElementArg[
|
|||||||
const needUpgrade = home.app.currentFeatures.maxWorkspacesPerOrg === 1;
|
const needUpgrade = home.app.currentFeatures.maxWorkspacesPerOrg === 1;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
menuItem(() => createDocAndOpen(home), menuIcon('Page'), t("CreateEmptyDocument"),
|
menuItem(() => createDocAndOpen(home), menuIcon('Page'), t("Create Empty Document"),
|
||||||
dom.cls('disabled', !home.newDocWorkspace.get()),
|
dom.cls('disabled', !home.newDocWorkspace.get()),
|
||||||
testId("dm-new-doc")
|
testId("dm-new-doc")
|
||||||
),
|
),
|
||||||
menuItem(() => importDocAndOpen(home), menuIcon('Import'), t("ImportDocument"),
|
menuItem(() => importDocAndOpen(home), menuIcon('Import'), t("Import Document"),
|
||||||
dom.cls('disabled', !home.newDocWorkspace.get()),
|
dom.cls('disabled', !home.newDocWorkspace.get()),
|
||||||
testId("dm-import")
|
testId("dm-import")
|
||||||
),
|
),
|
||||||
@ -195,7 +195,7 @@ function addMenu(home: HomeModel, creating: Observable<boolean>): DomElementArg[
|
|||||||
])),
|
])),
|
||||||
// For workspaces: if ACL says we can create them, but product says we can't,
|
// For workspaces: if ACL says we can create them, but product says we can't,
|
||||||
// then offer an upgrade link.
|
// then offer an upgrade link.
|
||||||
upgradableMenuItem(needUpgrade, () => creating.set(true), menuIcon('Folder'), t("CreateWorkspace"),
|
upgradableMenuItem(needUpgrade, () => creating.set(true), menuIcon('Folder'), t("Create Workspace"),
|
||||||
dom.cls('disabled', (use) => !roles.canEdit(orgAccess) || !use(home.available)),
|
dom.cls('disabled', (use) => !roles.canEdit(orgAccess) || !use(home.available)),
|
||||||
testId("dm-new-workspace")
|
testId("dm-new-workspace")
|
||||||
),
|
),
|
||||||
@ -205,9 +205,9 @@ function addMenu(home: HomeModel, creating: Observable<boolean>): DomElementArg[
|
|||||||
|
|
||||||
function workspaceMenu(home: HomeModel, ws: Workspace, renaming: Observable<Workspace|null>) {
|
function workspaceMenu(home: HomeModel, ws: Workspace, renaming: Observable<Workspace|null>) {
|
||||||
function deleteWorkspace() {
|
function deleteWorkspace() {
|
||||||
confirmModal(t('WorkspaceDeleteTitle', {workspace: ws.name}), t('Delete'),
|
confirmModal(t("Delete {{workspace}} and all included documents?", {workspace: ws.name}), t("Delete"),
|
||||||
() => home.deleteWorkspace(ws.id, false),
|
() => home.deleteWorkspace(ws.id, false),
|
||||||
t('WorkspaceDeleteText'));
|
t("Workspace will be moved to Trash."));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function manageWorkspaceUsers() {
|
async function manageWorkspaceUsers() {
|
||||||
@ -235,7 +235,7 @@ function workspaceMenu(home: HomeModel, ws: Workspace, renaming: Observable<Work
|
|||||||
// should formally be documented and defined in `Features`, with this check updated
|
// should formally be documented and defined in `Features`, with this check updated
|
||||||
// to look there instead.
|
// to look there instead.
|
||||||
home.app.isPersonal ? null : upgradableMenuItem(needUpgrade, manageWorkspaceUsers,
|
home.app.isPersonal ? null : upgradableMenuItem(needUpgrade, manageWorkspaceUsers,
|
||||||
roles.canEditAccess(ws.access) ? t("ManageUsers") : t("AccessDetails"),
|
roles.canEditAccess(ws.access) ? t("Manage Users") : t("Access Details"),
|
||||||
testId('dm-workspace-access')),
|
testId('dm-workspace-access')),
|
||||||
upgradeText(needUpgrade, () => home.app.showUpgradeModal()),
|
upgradeText(needUpgrade, () => home.app.showUpgradeModal()),
|
||||||
];
|
];
|
||||||
|
@ -34,7 +34,7 @@ export function createHelpTools(appModel: AppModel): DomContents {
|
|||||||
return cssSplitPageEntry(
|
return cssSplitPageEntry(
|
||||||
cssPageEntryMain(
|
cssPageEntryMain(
|
||||||
cssPageLink(cssPageIcon('Help'),
|
cssPageLink(cssPageIcon('Help'),
|
||||||
cssLinkText(t('HelpCenter')),
|
cssLinkText(t("Help Center")),
|
||||||
dom.cls('tour-help-center'),
|
dom.cls('tour-help-center'),
|
||||||
dom.on('click', (ev) => beaconOpenMessage({appModel})),
|
dom.on('click', (ev) => beaconOpenMessage({appModel})),
|
||||||
testId('left-feedback'),
|
testId('left-feedback'),
|
||||||
|
@ -26,29 +26,29 @@ export async function replaceTrunkWithFork(user: FullUser|null, doc: Document, a
|
|||||||
const trunkAccess = (await app.api.getDoc(origUrlId)).access;
|
const trunkAccess = (await app.api.getDoc(origUrlId)).access;
|
||||||
if (!roles.canEdit(trunkAccess)) {
|
if (!roles.canEdit(trunkAccess)) {
|
||||||
modal((ctl) => [
|
modal((ctl) => [
|
||||||
cssModalBody(t('CannotEditOriginal')),
|
cssModalBody(t("Replacing the original requires editing rights on the original document.")),
|
||||||
cssModalButtons(
|
cssModalButtons(
|
||||||
bigBasicButton(t('Cancel'), dom.on('click', () => ctl.close())),
|
bigBasicButton(t("Cancel"), dom.on('click', () => ctl.close())),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const docApi = app.api.getDocAPI(origUrlId);
|
const docApi = app.api.getDocAPI(origUrlId);
|
||||||
const cmp = await docApi.compareDoc(doc.id);
|
const cmp = await docApi.compareDoc(doc.id);
|
||||||
let titleText = t('UpdateOriginal');
|
let titleText = t("Update Original");
|
||||||
let buttonText = t('Update');
|
let buttonText = t("Update");
|
||||||
let warningText = t('WarningOriginalWillBeUpdated');
|
let warningText = t("The original version of this document will be updated.");
|
||||||
if (cmp.summary === 'left' || cmp.summary === 'both') {
|
if (cmp.summary === 'left' || cmp.summary === 'both') {
|
||||||
titleText = t('OriginalHasModifications');
|
titleText = t("Original Has Modifications");
|
||||||
buttonText = t('Overwrite');
|
buttonText = t("Overwrite");
|
||||||
warningText = `${warningText} ${t('WarningOverwriteOriginalChanges')}`;
|
warningText = `${warningText} ${t("Be careful, the original has changes not in this document. Those changes will be overwritten.")}`;
|
||||||
} else if (cmp.summary === 'unrelated') {
|
} else if (cmp.summary === 'unrelated') {
|
||||||
titleText = t('OriginalLooksUnrelated');
|
titleText = t("Original Looks Unrelated");
|
||||||
buttonText = t('Overwrite');
|
buttonText = t("Overwrite");
|
||||||
warningText = `${warningText} ${t('WarningWillBeOverwritten')}`;
|
warningText = `${warningText} ${t("It will be overwritten, losing any content not in this document.")}`;
|
||||||
} else if (cmp.summary === 'same') {
|
} else if (cmp.summary === 'same') {
|
||||||
titleText = 'Original Looks Identical';
|
titleText = t('Original Looks Identical');
|
||||||
warningText = `${warningText} ${t('WarningAlreadyIdentical')}`;
|
warningText = `${warningText} ${t("However, it appears to be already identical.")}`;
|
||||||
}
|
}
|
||||||
confirmModal(titleText, buttonText,
|
confirmModal(titleText, buttonText,
|
||||||
async () => {
|
async () => {
|
||||||
@ -66,8 +66,8 @@ function signupModal(message: string) {
|
|||||||
return modal((ctl) => [
|
return modal((ctl) => [
|
||||||
cssModalBody(message),
|
cssModalBody(message),
|
||||||
cssModalButtons(
|
cssModalButtons(
|
||||||
bigPrimaryButtonLink(t('SignUp'), {href: getLoginOrSignupUrl(), target: '_blank'}, testId('modal-signup')),
|
bigPrimaryButtonLink(t("Sign up"), {href: getLoginOrSignupUrl(), target: '_blank'}, testId('modal-signup')),
|
||||||
bigBasicButton(t('Cancel'), dom.on('click', () => ctl.close())),
|
bigBasicButton(t("Cancel"), dom.on('click', () => ctl.close())),
|
||||||
),
|
),
|
||||||
cssModalWidth('normal'),
|
cssModalWidth('normal'),
|
||||||
]);
|
]);
|
||||||
@ -96,7 +96,7 @@ function allowOtherOrgs(doc: Document, app: AppModel): boolean {
|
|||||||
*/
|
*/
|
||||||
export async function makeCopy(doc: Document, app: AppModel, modalTitle: string): Promise<void> {
|
export async function makeCopy(doc: Document, app: AppModel, modalTitle: string): Promise<void> {
|
||||||
if (!app.currentValidUser) {
|
if (!app.currentValidUser) {
|
||||||
signupModal(t('ToSaveSignUpAndReload'));
|
signupModal(t("To save your changes, please sign up, then reload this page."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let orgs = allowOtherOrgs(doc, app) ? await app.api.getOrgs(true) : null;
|
let orgs = allowOtherOrgs(doc, app) ? await app.api.getOrgs(true) : null;
|
||||||
@ -150,7 +150,7 @@ class SaveCopyModal extends Disposable {
|
|||||||
|
|
||||||
public async save() {
|
public async save() {
|
||||||
const ws = this._destWS.get();
|
const ws = this._destWS.get();
|
||||||
if (!ws) { throw new Error(t('NoDestinationWorkspace')); }
|
if (!ws) { throw new Error(t("No destination workspace")); }
|
||||||
const api = this._app.api;
|
const api = this._app.api;
|
||||||
const org = this._destOrg.get();
|
const org = this._destOrg.get();
|
||||||
const docWorker = await api.getWorkerAPI('import');
|
const docWorker = await api.getWorkerAPI('import');
|
||||||
@ -173,7 +173,7 @@ class SaveCopyModal extends Disposable {
|
|||||||
return [
|
return [
|
||||||
cssField(
|
cssField(
|
||||||
cssLabel(t("Name")),
|
cssLabel(t("Name")),
|
||||||
input(this._destName, {onInput: true}, {placeholder: t('EnterDocumentName')}, dom.cls(cssInput.className),
|
input(this._destName, {onInput: true}, {placeholder: t("Enter document name")}, dom.cls(cssInput.className),
|
||||||
// modal dialog grabs focus after 10ms delay; so to focus this input, wait a bit longer
|
// modal dialog grabs focus after 10ms delay; so to focus this input, wait a bit longer
|
||||||
// (see the TODO in app/client/ui2018/modals.ts about weasel.js and focus).
|
// (see the TODO in app/client/ui2018/modals.ts about weasel.js and focus).
|
||||||
(elem) => { setTimeout(() => { elem.focus(); }, 20); },
|
(elem) => { setTimeout(() => { elem.focus(); }, 20); },
|
||||||
@ -181,8 +181,8 @@ class SaveCopyModal extends Disposable {
|
|||||||
testId('copy-dest-name'))
|
testId('copy-dest-name'))
|
||||||
),
|
),
|
||||||
cssField(
|
cssField(
|
||||||
cssLabel(t("AsTemplate")),
|
cssLabel(t("As Template")),
|
||||||
cssCheckbox(this._asTemplate, t('IncludeStructureWithoutData'),
|
cssCheckbox(this._asTemplate, t("Include the structure without any of the data."),
|
||||||
testId('save-as-template'))
|
testId('save-as-template'))
|
||||||
),
|
),
|
||||||
// Show the team picker only when saving to other teams is allowed and there are other teams
|
// Show the team picker only when saving to other teams is allowed and there are other teams
|
||||||
@ -199,7 +199,7 @@ class SaveCopyModal extends Disposable {
|
|||||||
// Show the workspace picker only when destOrg is a team site, because personal orgs do not have workspaces.
|
// Show the workspace picker only when destOrg is a team site, because personal orgs do not have workspaces.
|
||||||
dom.domComputed((use) => use(this._showWorkspaces) && use(this._workspaces), (wss) =>
|
dom.domComputed((use) => use(this._showWorkspaces) && use(this._workspaces), (wss) =>
|
||||||
wss === false ? null :
|
wss === false ? null :
|
||||||
wss && wss.length === 0 ? cssWarningText(t("NoWriteAccessToSite"),
|
wss && wss.length === 0 ? cssWarningText(t("You do not have write access to this site"),
|
||||||
testId('copy-warning')) :
|
testId('copy-warning')) :
|
||||||
[
|
[
|
||||||
cssField(
|
cssField(
|
||||||
@ -216,7 +216,7 @@ class SaveCopyModal extends Disposable {
|
|||||||
),
|
),
|
||||||
wss ? dom.domComputed(this._destWS, (destWs) =>
|
wss ? dom.domComputed(this._destWS, (destWs) =>
|
||||||
destWs && !roles.canEdit(destWs.access) ?
|
destWs && !roles.canEdit(destWs.access) ?
|
||||||
cssWarningText(t("NoWriteAccessToWorkspace"),
|
cssWarningText(t("You do not have write access to the selected workspace"),
|
||||||
testId('copy-warning')
|
testId('copy-warning')
|
||||||
) : null
|
) : null
|
||||||
) : null
|
) : null
|
||||||
|
@ -24,10 +24,10 @@ function buildAction(action: NotifyAction, item: Notification, options: IBeaconO
|
|||||||
switch (action) {
|
switch (action) {
|
||||||
case 'upgrade':
|
case 'upgrade':
|
||||||
if (appModel) {
|
if (appModel) {
|
||||||
return cssToastAction(t('UpgradePlan'), dom.on('click', () =>
|
return cssToastAction(t("Upgrade Plan"), dom.on('click', () =>
|
||||||
appModel.showUpgradeModal()));
|
appModel.showUpgradeModal()));
|
||||||
} else {
|
} else {
|
||||||
return dom('a', cssToastAction.cls(''), t('UpgradePlan'), {target: '_blank'},
|
return dom('a', cssToastAction.cls(''), t("Upgrade Plan"), {target: '_blank'},
|
||||||
{href: commonUrls.plans});
|
{href: commonUrls.plans});
|
||||||
}
|
}
|
||||||
case 'renew':
|
case 'renew':
|
||||||
@ -37,22 +37,22 @@ function buildAction(action: NotifyAction, item: Notification, options: IBeaconO
|
|||||||
if (appModel && appModel.currentOrg && appModel.currentOrg.billingAccount &&
|
if (appModel && appModel.currentOrg && appModel.currentOrg.billingAccount &&
|
||||||
!appModel.currentOrg.billingAccount.isManager) { return null; }
|
!appModel.currentOrg.billingAccount.isManager) { return null; }
|
||||||
// Otherwise return a link to the billing page.
|
// Otherwise return a link to the billing page.
|
||||||
return dom('a', cssToastAction.cls(''), t('Renew'), {target: '_blank'},
|
return dom('a', cssToastAction.cls(''), t("Renew"), {target: '_blank'},
|
||||||
{href: urlState().makeUrl({billing: 'billing'})});
|
{href: urlState().makeUrl({billing: 'billing'})});
|
||||||
|
|
||||||
case 'personal':
|
case 'personal':
|
||||||
if (!appModel) { return null; }
|
if (!appModel) { return null; }
|
||||||
return cssToastAction(t('GoToPersonalSite'), dom.on('click', async () => {
|
return cssToastAction(t("Go to your free personal site"), dom.on('click', async () => {
|
||||||
const info = await appModel.api.getSessionAll();
|
const info = await appModel.api.getSessionAll();
|
||||||
const orgs = info.orgs.filter(org => org.owner && org.owner.id === appModel.currentUser?.id);
|
const orgs = info.orgs.filter(org => org.owner && org.owner.id === appModel.currentUser?.id);
|
||||||
if (orgs.length !== 1) {
|
if (orgs.length !== 1) {
|
||||||
throw new Error(t('ErrorCannotFindPersonalSite'));
|
throw new Error(t("Cannot find personal site, sorry!"));
|
||||||
}
|
}
|
||||||
window.location.assign(urlState().makeUrl({org: orgs[0].domain || undefined}));
|
window.location.assign(urlState().makeUrl({org: orgs[0].domain || undefined}));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
case 'report-problem':
|
case 'report-problem':
|
||||||
return cssToastAction(t('ReportProblem'), testId('toast-report-problem'),
|
return cssToastAction(t("Report a problem"), testId('toast-report-problem'),
|
||||||
dom.on('click', () => beaconOpenMessage({...options, includeAppErrors: true})));
|
dom.on('click', () => beaconOpenMessage({...options, includeAppErrors: true})));
|
||||||
|
|
||||||
case 'ask-for-help': {
|
case 'ask-for-help': {
|
||||||
@ -60,7 +60,7 @@ function buildAction(action: NotifyAction, item: Notification, options: IBeaconO
|
|||||||
error: new Error(item.options.message as string),
|
error: new Error(item.options.message as string),
|
||||||
timestamp: item.options.timestamp,
|
timestamp: item.options.timestamp,
|
||||||
}];
|
}];
|
||||||
return cssToastAction(t('AskForHelp'),
|
return cssToastAction(t("Ask for help"),
|
||||||
dom.on('click', () => beaconOpenMessage({...options, includeAppErrors: true, errors})));
|
dom.on('click', () => beaconOpenMessage({...options, includeAppErrors: true, errors})));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,11 +154,11 @@ function buildNotifyDropdown(ctl: IOpenController, notifier: Notifier, appModel:
|
|||||||
|
|
||||||
cssDropdownContent(
|
cssDropdownContent(
|
||||||
cssDropdownHeader(
|
cssDropdownHeader(
|
||||||
cssDropdownHeaderTitle(t('Notifications')),
|
cssDropdownHeaderTitle(t("Notifications")),
|
||||||
shouldHideUiElement("helpCenter") ? null :
|
shouldHideUiElement("helpCenter") ? null :
|
||||||
cssDropdownFeedbackLink(
|
cssDropdownFeedbackLink(
|
||||||
cssDropdownFeedbackIcon('Feedback'),
|
cssDropdownFeedbackIcon('Feedback'),
|
||||||
t('GiveFeedback'),
|
t("Give feedback"),
|
||||||
dom.on('click', () => beaconOpenMessage({appModel, onOpen: () => ctl.close(), route: '/ask/message/'})),
|
dom.on('click', () => beaconOpenMessage({appModel, onOpen: () => ctl.close(), route: '/ask/message/'})),
|
||||||
testId('feedback'),
|
testId('feedback'),
|
||||||
)
|
)
|
||||||
@ -171,7 +171,7 @@ function buildNotifyDropdown(ctl: IOpenController, notifier: Notifier, appModel:
|
|||||||
),
|
),
|
||||||
dom.maybe((use) => use(dropdownItems).length === 0 && !use(disconnectMsg), () =>
|
dom.maybe((use) => use(dropdownItems).length === 0 && !use(disconnectMsg), () =>
|
||||||
cssDropdownStatus(
|
cssDropdownStatus(
|
||||||
dom('div', cssDropdownStatusText(t('NoNotifications'))),
|
dom('div', cssDropdownStatusText(t("No notifications"))),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
dom.forEach(dropdownItems, item =>
|
dom.forEach(dropdownItems, item =>
|
||||||
|
@ -299,7 +299,7 @@ class OnBoardingPopupsCtl extends Disposable {
|
|||||||
{style: `margin-right: 8px; visibility: ${isFirstStep ? 'hidden' : 'visible'}`},
|
{style: `margin-right: 8px; visibility: ${isFirstStep ? 'hidden' : 'visible'}`},
|
||||||
),
|
),
|
||||||
bigPrimaryButton(
|
bigPrimaryButton(
|
||||||
isLastStep ? t('Finish') : t('Next'), testId('next'),
|
isLastStep ? t("Finish") : t("Next"), testId('next'),
|
||||||
dom.on('click', () => this._move(+1, true)),
|
dom.on('click', () => this._move(+1, true)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -28,7 +28,7 @@ const testId = makeTestId('test-video-tour-');
|
|||||||
cssVideo(
|
cssVideo(
|
||||||
{
|
{
|
||||||
src: commonUrls.videoTour,
|
src: commonUrls.videoTour,
|
||||||
title: t('YouTubeVideoPlayer'),
|
title: t("YouTube video player"),
|
||||||
frameborder: '0',
|
frameborder: '0',
|
||||||
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
||||||
allowfullscreen: '',
|
allowfullscreen: '',
|
||||||
@ -51,7 +51,7 @@ const testId = makeTestId('test-video-tour-');
|
|||||||
export function createVideoTourTextButton(): HTMLDivElement {
|
export function createVideoTourTextButton(): HTMLDivElement {
|
||||||
const elem: HTMLDivElement = cssVideoTourTextButton(
|
const elem: HTMLDivElement = cssVideoTourTextButton(
|
||||||
cssVideoIcon('Video'),
|
cssVideoIcon('Video'),
|
||||||
t('GristVideoTour'),
|
t("Grist Video Tour"),
|
||||||
dom.on('click', () => openVideoTour(elem)),
|
dom.on('click', () => openVideoTour(elem)),
|
||||||
testId('text-button'),
|
testId('text-button'),
|
||||||
);
|
);
|
||||||
@ -77,7 +77,7 @@ export function createVideoTourToolsButton(): HTMLDivElement | null {
|
|||||||
dom.autoDispose(commandsGroup),
|
dom.autoDispose(commandsGroup),
|
||||||
cssPageLink(
|
cssPageLink(
|
||||||
iconElement = cssPageIcon('Video'),
|
iconElement = cssPageIcon('Video'),
|
||||||
cssLinkText(t('VideoTour')),
|
cssLinkText(t("Video Tour")),
|
||||||
dom.cls('tour-help-center'),
|
dom.cls('tour-help-center'),
|
||||||
dom.on('click', () => openVideoTour(iconElement)),
|
dom.on('click', () => openVideoTour(iconElement)),
|
||||||
testId('tools-button'),
|
testId('tools-button'),
|
||||||
|
@ -183,7 +183,7 @@ export function buildPageWidgetPicker(
|
|||||||
// should be handle by the caller.
|
// should be handle by the caller.
|
||||||
if (await isLongerThan(savePromise, DELAY_BEFORE_SPINNER_MS)) {
|
if (await isLongerThan(savePromise, DELAY_BEFORE_SPINNER_MS)) {
|
||||||
const label = getWidgetTypes(type).label;
|
const label = getWidgetTypes(type).label;
|
||||||
await spinnerModal(t('BuildingWidget', { label }), savePromise);
|
await spinnerModal(t("Building {{- label}} widget", { label }), savePromise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ export class PageWidgetSelect extends Disposable {
|
|||||||
testId('container'),
|
testId('container'),
|
||||||
cssBody(
|
cssBody(
|
||||||
cssPanel(
|
cssPanel(
|
||||||
header(t('SelectWidget')),
|
header(t("Select Widget")),
|
||||||
sectionTypes.map((value) => {
|
sectionTypes.map((value) => {
|
||||||
const {label, icon: iconName} = getWidgetTypes(value);
|
const {label, icon: iconName} = getWidgetTypes(value);
|
||||||
const disabled = computed(this._value.table, (use, tid) => this._isTypeDisabled(value, tid));
|
const disabled = computed(this._value.table, (use, tid) => this._isTypeDisabled(value, tid));
|
||||||
@ -302,7 +302,7 @@ export class PageWidgetSelect extends Disposable {
|
|||||||
),
|
),
|
||||||
cssPanel(
|
cssPanel(
|
||||||
testId('data'),
|
testId('data'),
|
||||||
header(t('SelectData')),
|
header(t("Select Data")),
|
||||||
cssEntry(
|
cssEntry(
|
||||||
cssIcon('TypeTable'), 'New Table',
|
cssIcon('TypeTable'), 'New Table',
|
||||||
// prevent the selection of 'New Table' if it is disabled
|
// prevent the selection of 'New Table' if it is disabled
|
||||||
@ -336,7 +336,7 @@ export class PageWidgetSelect extends Disposable {
|
|||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
cssPanel(
|
cssPanel(
|
||||||
header(t('GroupBy')),
|
header(t("Group by")),
|
||||||
dom.hide((use) => !use(this._value.summarize)),
|
dom.hide((use) => !use(this._value.summarize)),
|
||||||
domComputed(
|
domComputed(
|
||||||
(use) => use(this._columns)
|
(use) => use(this._columns)
|
||||||
@ -378,7 +378,7 @@ export class PageWidgetSelect extends Disposable {
|
|||||||
bigPrimaryButton(
|
bigPrimaryButton(
|
||||||
// TODO: The button's label of the page widget picker should read 'Close' instead when
|
// TODO: The button's label of the page widget picker should read 'Close' instead when
|
||||||
// there are no changes.
|
// there are no changes.
|
||||||
this._options.buttonLabel || t('AddToPage'),
|
this._options.buttonLabel || t("Add to Page"),
|
||||||
dom.prop('disabled', (use) => !isValidSelection(
|
dom.prop('disabled', (use) => !isValidSelection(
|
||||||
use(this._value.table), use(this._value.type), this._options.isNewPage)
|
use(this._value.table), use(this._value.type), this._options.isNewPage)
|
||||||
),
|
),
|
||||||
|
@ -137,12 +137,12 @@ function buildPrompt(tableNames: string[], onSave: (option: RemoveOption) => Pro
|
|||||||
const saveDisabled = Computed.create(owner, use => use(selected) === '');
|
const saveDisabled = Computed.create(owner, use => use(selected) === '');
|
||||||
const saveFunc = () => onSave(selected.get());
|
const saveFunc = () => onSave(selected.get());
|
||||||
return {
|
return {
|
||||||
title: t('TableWillNoLongerBeVisible', { count: tableNames.length }),
|
title: t('The following tables will no longer be visible', { count: tableNames.length }),
|
||||||
body: dom('div',
|
body: dom('div',
|
||||||
testId('popup'),
|
testId('popup'),
|
||||||
buildWarning(tableNames),
|
buildWarning(tableNames),
|
||||||
cssOptions(
|
cssOptions(
|
||||||
buildOption(selected, 'data', t('DeleteDataAndPage')),
|
buildOption(selected, 'data', t("Delete data and this page.")),
|
||||||
buildOption(selected, 'page',
|
buildOption(selected, 'page',
|
||||||
[ // TODO i18n
|
[ // TODO i18n
|
||||||
`Keep data and delete page. `,
|
`Keep data and delete page. `,
|
||||||
@ -153,7 +153,7 @@ function buildPrompt(tableNames: string[], onSave: (option: RemoveOption) => Pro
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
saveDisabled,
|
saveDisabled,
|
||||||
saveLabel: t('Delete'),
|
saveLabel: t("Delete"),
|
||||||
saveFunc,
|
saveFunc,
|
||||||
width: 'fixed-wide',
|
width: 'fixed-wide',
|
||||||
extraButtons: [],
|
extraButtons: [],
|
||||||
|
@ -56,11 +56,11 @@ const PageSubTab = StringUnion("widget", "sortAndFilter", "data");
|
|||||||
export function getFieldType(widgetType: IWidgetType|null) {
|
export function getFieldType(widgetType: IWidgetType|null) {
|
||||||
// A map of widget type to the icon and label to use for a field of that widget.
|
// A map of widget type to the icon and label to use for a field of that widget.
|
||||||
const fieldTypes = new Map<IWidgetType, {label: string, icon: IconName, pluralLabel: string}>([
|
const fieldTypes = new Map<IWidgetType, {label: string, icon: IconName, pluralLabel: string}>([
|
||||||
['record', {label: t('Column', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Column', { count: 2 })}],
|
['record', {label: t('Columns', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Columns', { count: 2 })}],
|
||||||
['detail', {label: t('Field', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Field', { count: 2 })}],
|
['detail', {label: t('Fields', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Fields', { count: 2 })}],
|
||||||
['single', {label: t('Field', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Field', { count: 2 })}],
|
['single', {label: t('Fields', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Fields', { count: 2 })}],
|
||||||
['chart', {label: t('Series', { count: 1 }), icon: 'ChartLine', pluralLabel: t('Series', { count: 2 })}],
|
['chart', {label: t('Series', { count: 1 }), icon: 'ChartLine', pluralLabel: t('Series', { count: 2 })}],
|
||||||
['custom', {label: t('Column', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Column', { count: 2 })}],
|
['custom', {label: t('Columns', { count: 1 }), icon: 'TypeCell', pluralLabel: t('Columns', { count: 2 })}],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return fieldTypes.get(widgetType || 'record') || fieldTypes.get('record')!;
|
return fieldTypes.get(widgetType || 'record') || fieldTypes.get('record')!;
|
||||||
@ -242,7 +242,7 @@ export class RightPanel extends Disposable {
|
|||||||
),
|
),
|
||||||
cssSeparator(),
|
cssSeparator(),
|
||||||
dom.maybe<FieldBuilder|null>(fieldBuilder, builder => [
|
dom.maybe<FieldBuilder|null>(fieldBuilder, builder => [
|
||||||
cssLabel(t('ColumnType')),
|
cssLabel(t("COLUMN TYPE")),
|
||||||
cssSection(
|
cssSection(
|
||||||
builder.buildSelectTypeDom(),
|
builder.buildSelectTypeDom(),
|
||||||
),
|
),
|
||||||
@ -265,7 +265,7 @@ export class RightPanel extends Disposable {
|
|||||||
cssRow(refSelect.buildDom()),
|
cssRow(refSelect.buildDom()),
|
||||||
cssSeparator()
|
cssSeparator()
|
||||||
]),
|
]),
|
||||||
cssLabel(t('Transform')),
|
cssLabel(t("TRANSFORM")),
|
||||||
dom.maybe<FieldBuilder|null>(fieldBuilder, builder => builder.buildTransformDom()),
|
dom.maybe<FieldBuilder|null>(fieldBuilder, builder => builder.buildTransformDom()),
|
||||||
dom.maybe(isMultiSelect, () => disabledSection()),
|
dom.maybe(isMultiSelect, () => disabledSection()),
|
||||||
testId('panel-transform'),
|
testId('panel-transform'),
|
||||||
@ -295,15 +295,15 @@ export class RightPanel extends Disposable {
|
|||||||
private _buildPageWidgetContent(_owner: MultiHolder) {
|
private _buildPageWidgetContent(_owner: MultiHolder) {
|
||||||
return [
|
return [
|
||||||
cssSubTabContainer(
|
cssSubTabContainer(
|
||||||
cssSubTab(t('Widget'),
|
cssSubTab(t("Widget"),
|
||||||
cssSubTab.cls('-selected', (use) => use(this._subTab) === 'widget'),
|
cssSubTab.cls('-selected', (use) => use(this._subTab) === 'widget'),
|
||||||
dom.on('click', () => this._subTab.set("widget")),
|
dom.on('click', () => this._subTab.set("widget")),
|
||||||
testId('config-widget')),
|
testId('config-widget')),
|
||||||
cssSubTab(t('SortAndFilter'),
|
cssSubTab(t("Sort & Filter"),
|
||||||
cssSubTab.cls('-selected', (use) => use(this._subTab) === 'sortAndFilter'),
|
cssSubTab.cls('-selected', (use) => use(this._subTab) === 'sortAndFilter'),
|
||||||
dom.on('click', () => this._subTab.set("sortAndFilter")),
|
dom.on('click', () => this._subTab.set("sortAndFilter")),
|
||||||
testId('config-sortAndFilter')),
|
testId('config-sortAndFilter')),
|
||||||
cssSubTab(t('Data'),
|
cssSubTab(t("Data"),
|
||||||
cssSubTab.cls('-selected', (use) => use(this._subTab) === 'data'),
|
cssSubTab.cls('-selected', (use) => use(this._subTab) === 'data'),
|
||||||
dom.on('click', () => this._subTab.set("data")),
|
dom.on('click', () => this._subTab.set("data")),
|
||||||
testId('config-data')),
|
testId('config-data')),
|
||||||
@ -345,7 +345,7 @@ export class RightPanel extends Disposable {
|
|||||||
});
|
});
|
||||||
return dom.maybe(viewConfigTab, (vct) => [
|
return dom.maybe(viewConfigTab, (vct) => [
|
||||||
this._disableIfReadonly(),
|
this._disableIfReadonly(),
|
||||||
cssLabel(dom.text(use => use(activeSection.isRaw) ? t('DataTableName') : t('WidgetTitle')),
|
cssLabel(dom.text(use => use(activeSection.isRaw) ? t("DATA TABLE NAME") : t("WIDGET TITLE")),
|
||||||
dom.style('margin-bottom', '14px'),
|
dom.style('margin-bottom', '14px'),
|
||||||
),
|
),
|
||||||
cssRow(cssTextInput(
|
cssRow(cssTextInput(
|
||||||
@ -362,7 +362,7 @@ export class RightPanel extends Disposable {
|
|||||||
dom.maybe(
|
dom.maybe(
|
||||||
(use) => !use(activeSection.isRaw),
|
(use) => !use(activeSection.isRaw),
|
||||||
() => cssRow(
|
() => cssRow(
|
||||||
primaryButton(t('ChangeWidget'), this._createPageWidgetPicker()),
|
primaryButton(t("Change Widget"), this._createPageWidgetPicker()),
|
||||||
cssRow.cls('-top-space')
|
cssRow.cls('-top-space')
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -370,7 +370,7 @@ export class RightPanel extends Disposable {
|
|||||||
cssSeparator(),
|
cssSeparator(),
|
||||||
|
|
||||||
dom.maybe((use) => ['detail', 'single'].includes(use(this._pageWidgetType)!), () => [
|
dom.maybe((use) => ['detail', 'single'].includes(use(this._pageWidgetType)!), () => [
|
||||||
cssLabel(t('Theme')),
|
cssLabel(t("Theme")),
|
||||||
dom('div',
|
dom('div',
|
||||||
vct._buildThemeDom(),
|
vct._buildThemeDom(),
|
||||||
vct._buildLayoutDom())
|
vct._buildLayoutDom())
|
||||||
@ -385,22 +385,22 @@ export class RightPanel extends Disposable {
|
|||||||
if (use(this._pageWidgetType) !== 'record') { return null; }
|
if (use(this._pageWidgetType) !== 'record') { return null; }
|
||||||
return [
|
return [
|
||||||
cssSeparator(),
|
cssSeparator(),
|
||||||
cssLabel(t('RowStyleUpper')),
|
cssLabel(t("ROW STYLE")),
|
||||||
domAsync(imports.loadViewPane().then(ViewPane =>
|
domAsync(imports.loadViewPane().then(ViewPane =>
|
||||||
dom.create(ViewPane.ConditionalStyle, t("RowStyle"), activeSection, this._gristDoc)
|
dom.create(ViewPane.ConditionalStyle, t("Row Style"), activeSection, this._gristDoc)
|
||||||
))
|
))
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
dom.maybe((use) => use(this._pageWidgetType) === 'chart', () => [
|
dom.maybe((use) => use(this._pageWidgetType) === 'chart', () => [
|
||||||
cssLabel(t('ChartType')),
|
cssLabel(t("CHART TYPE")),
|
||||||
vct._buildChartConfigDom(),
|
vct._buildChartConfigDom(),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
dom.maybe((use) => use(this._pageWidgetType) === 'custom', () => {
|
dom.maybe((use) => use(this._pageWidgetType) === 'custom', () => {
|
||||||
const parts = vct._buildCustomTypeItems() as any[];
|
const parts = vct._buildCustomTypeItems() as any[];
|
||||||
return [
|
return [
|
||||||
cssLabel(t('Custom')),
|
cssLabel(t("CUSTOM")),
|
||||||
// If 'customViewPlugin' feature is on, show the toggle that allows switching to
|
// If 'customViewPlugin' feature is on, show the toggle that allows switching to
|
||||||
// plugin mode. Note that the default mode for a new 'custom' view is 'url', so that's
|
// plugin mode. Note that the default mode for a new 'custom' view is 'url', so that's
|
||||||
// the only one that will be shown without the feature flag.
|
// the only one that will be shown without the feature flag.
|
||||||
@ -465,15 +465,15 @@ export class RightPanel extends Disposable {
|
|||||||
link.onWrite((val) => this._gristDoc.saveLink(val));
|
link.onWrite((val) => this._gristDoc.saveLink(val));
|
||||||
return [
|
return [
|
||||||
this._disableIfReadonly(),
|
this._disableIfReadonly(),
|
||||||
cssLabel(t('DataTable')),
|
cssLabel(t("DATA TABLE")),
|
||||||
cssRow(
|
cssRow(
|
||||||
cssIcon('TypeTable'), cssDataLabel(t('SourceData')),
|
cssIcon('TypeTable'), cssDataLabel(t("SOURCE DATA")),
|
||||||
cssContent(dom.text((use) => use(use(table).primaryTableId)),
|
cssContent(dom.text((use) => use(use(table).primaryTableId)),
|
||||||
testId('pwc-table'))
|
testId('pwc-table'))
|
||||||
),
|
),
|
||||||
dom(
|
dom(
|
||||||
'div',
|
'div',
|
||||||
cssRow(cssIcon('Pivot'), cssDataLabel(t('GroupedBy'))),
|
cssRow(cssIcon('Pivot'), cssDataLabel(t("GROUPED BY"))),
|
||||||
cssRow(domComputed(groupedBy, (cols) => cssList(cols.map((c) => (
|
cssRow(domComputed(groupedBy, (cols) => cssList(cols.map((c) => (
|
||||||
cssListItem(dom.text(c.label),
|
cssListItem(dom.text(c.label),
|
||||||
testId('pwc-groupedBy-col'))
|
testId('pwc-groupedBy-col'))
|
||||||
@ -485,12 +485,12 @@ export class RightPanel extends Disposable {
|
|||||||
),
|
),
|
||||||
|
|
||||||
dom.maybe((use) => !use(activeSection.isRaw), () =>
|
dom.maybe((use) => !use(activeSection.isRaw), () =>
|
||||||
cssButtonRow(primaryButton(t('EditDataSelection'), this._createPageWidgetPicker(),
|
cssButtonRow(primaryButton(t("Edit Data Selection"), this._createPageWidgetPicker(),
|
||||||
testId('pwc-editDataSelection')),
|
testId('pwc-editDataSelection')),
|
||||||
dom.maybe(
|
dom.maybe(
|
||||||
use => Boolean(use(use(activeSection.table).summarySourceTable)),
|
use => Boolean(use(use(activeSection.table).summarySourceTable)),
|
||||||
() => basicButton(
|
() => basicButton(
|
||||||
t('Detach'),
|
t("Detach"),
|
||||||
dom.on('click', () => this._gristDoc.docData.sendAction(
|
dom.on('click', () => this._gristDoc.docData.sendAction(
|
||||||
["DetachSummaryViewSection", activeSection.getRowId()])),
|
["DetachSummaryViewSection", activeSection.getRowId()])),
|
||||||
testId('detach-button'),
|
testId('detach-button'),
|
||||||
@ -507,10 +507,10 @@ export class RightPanel extends Disposable {
|
|||||||
cssSeparator(),
|
cssSeparator(),
|
||||||
|
|
||||||
dom.maybe((use) => !use(activeSection.isRaw), () => [
|
dom.maybe((use) => !use(activeSection.isRaw), () => [
|
||||||
cssLabel(t('SelectBy')),
|
cssLabel(t("SELECT BY")),
|
||||||
cssRow(
|
cssRow(
|
||||||
dom.update(
|
dom.update(
|
||||||
select(link, linkOptions, {defaultLabel: t('SelectWidget')}),
|
select(link, linkOptions, {defaultLabel: t("Select Widget")}),
|
||||||
dom.on('click', () => {
|
dom.on('click', () => {
|
||||||
refreshTrigger.set(!refreshTrigger.get());
|
refreshTrigger.set(!refreshTrigger.get());
|
||||||
})
|
})
|
||||||
@ -526,7 +526,7 @@ export class RightPanel extends Disposable {
|
|||||||
// TODO: sections should be listed following the order of appearance in the view layout (ie:
|
// TODO: sections should be listed following the order of appearance in the view layout (ie:
|
||||||
// left/right - top/bottom);
|
// left/right - top/bottom);
|
||||||
return selectorFor.length ? [
|
return selectorFor.length ? [
|
||||||
cssLabel(t('SelectorFor'), testId('selector-for')),
|
cssLabel(t("SELECTOR FOR"), testId('selector-for')),
|
||||||
cssRow(cssList(selectorFor.map((sec) => this._buildSectionItem(sec))))
|
cssRow(cssList(selectorFor.map((sec) => this._buildSectionItem(sec))))
|
||||||
] : null;
|
] : null;
|
||||||
}),
|
}),
|
||||||
@ -538,7 +538,7 @@ export class RightPanel extends Disposable {
|
|||||||
const section = gristDoc.viewModel.activeSection;
|
const section = gristDoc.viewModel.activeSection;
|
||||||
const onSave = (val: IPageWidget) => gristDoc.saveViewSection(section.peek(), val);
|
const onSave = (val: IPageWidget) => gristDoc.saveViewSection(section.peek(), val);
|
||||||
return (elem) => { attachPageWidgetPicker(elem, gristDoc, onSave, {
|
return (elem) => { attachPageWidgetPicker(elem, gristDoc, onSave, {
|
||||||
buttonLabel: t('Save'),
|
buttonLabel: t("Save"),
|
||||||
value: () => toPageWidget(section.peek()),
|
value: () => toPageWidget(section.peek()),
|
||||||
selectBy: (val) => gristDoc.selectBy(val),
|
selectBy: (val) => gristDoc.selectBy(val),
|
||||||
}); };
|
}); };
|
||||||
@ -559,7 +559,7 @@ export class RightPanel extends Disposable {
|
|||||||
return dom.maybe(this._gristDoc.docPageModel.isReadonly, () => (
|
return dom.maybe(this._gristDoc.docPageModel.isReadonly, () => (
|
||||||
cssOverlay(
|
cssOverlay(
|
||||||
testId('disable-overlay'),
|
testId('disable-overlay'),
|
||||||
cssBottomText(t('NoEditAccess')),
|
cssBottomText(t("You do not have edit access to this document")),
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -19,29 +19,29 @@ export function RowContextMenu({ disableInsert, disableDelete, isViewSorted, num
|
|||||||
// bottom. It could be very confusing for users who might expect the record to stay above or
|
// bottom. It could be very confusing for users who might expect the record to stay above or
|
||||||
// below the active row. Thus in this case we show a single `insert row` command.
|
// below the active row. Thus in this case we show a single `insert row` command.
|
||||||
result.push(
|
result.push(
|
||||||
menuItemCmd(allCommands.insertRecordAfter, t('InsertRow'),
|
menuItemCmd(allCommands.insertRecordAfter, t("Insert row"),
|
||||||
dom.cls('disabled', disableInsert)),
|
dom.cls('disabled', disableInsert)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result.push(
|
result.push(
|
||||||
menuItemCmd(allCommands.insertRecordBefore, t('InsertRowAbove'),
|
menuItemCmd(allCommands.insertRecordBefore, t("Insert row above"),
|
||||||
dom.cls('disabled', disableInsert)),
|
dom.cls('disabled', disableInsert)),
|
||||||
menuItemCmd(allCommands.insertRecordAfter, t('InsertRowBelow'),
|
menuItemCmd(allCommands.insertRecordAfter, t("Insert row below"),
|
||||||
dom.cls('disabled', disableInsert)),
|
dom.cls('disabled', disableInsert)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
result.push(
|
result.push(
|
||||||
menuItemCmd(allCommands.duplicateRows, t('DuplicateRows', { count: numRows }),
|
menuItemCmd(allCommands.duplicateRows, t('Duplicate rows', { count: numRows }),
|
||||||
dom.cls('disabled', disableInsert || numRows === 0)),
|
dom.cls('disabled', disableInsert || numRows === 0)),
|
||||||
);
|
);
|
||||||
result.push(
|
result.push(
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
// TODO: should show `Delete ${num} rows` when multiple are selected
|
// TODO: should show `Delete ${num} rows` when multiple are selected
|
||||||
menuItemCmd(allCommands.deleteRecords, t('Delete'),
|
menuItemCmd(allCommands.deleteRecords, t("Delete"),
|
||||||
dom.cls('disabled', disableDelete)),
|
dom.cls('disabled', disableDelete)),
|
||||||
);
|
);
|
||||||
result.push(
|
result.push(
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuItemCmd(allCommands.copyLink, t('CopyAnchorLink')));
|
menuItemCmd(allCommands.copyLink, t("Copy anchor link")));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -35,18 +35,18 @@ export function buildShareMenuButton(pageModel: DocPageModel): DomContents {
|
|||||||
// available (a user quick enough to open the menu in this state would have to re-open it).
|
// available (a user quick enough to open the menu in this state would have to re-open it).
|
||||||
return dom.maybe(pageModel.currentDoc, (doc) => {
|
return dom.maybe(pageModel.currentDoc, (doc) => {
|
||||||
const appModel = pageModel.appModel;
|
const appModel = pageModel.appModel;
|
||||||
const saveCopy = () => makeCopy(doc, appModel, t('SaveDocument')).catch(reportError);
|
const saveCopy = () => makeCopy(doc, appModel, t("Save Document")).catch(reportError);
|
||||||
if (doc.idParts.snapshotId) {
|
if (doc.idParts.snapshotId) {
|
||||||
const backToCurrent = () => urlState().pushUrl({doc: buildOriginalUrlId(doc.id, true)});
|
const backToCurrent = () => urlState().pushUrl({doc: buildOriginalUrlId(doc.id, true)});
|
||||||
return shareButton(t('BackToCurrent'), () => [
|
return shareButton(t("Back to Current"), () => [
|
||||||
menuManageUsers(doc, pageModel),
|
menuManageUsers(doc, pageModel),
|
||||||
menuSaveCopy(t('SaveCopy'), doc, appModel),
|
menuSaveCopy(t("Save Copy"), doc, appModel),
|
||||||
menuOriginal(doc, appModel, true),
|
menuOriginal(doc, appModel, true),
|
||||||
menuExports(doc, pageModel),
|
menuExports(doc, pageModel),
|
||||||
], {buttonAction: backToCurrent});
|
], {buttonAction: backToCurrent});
|
||||||
} else if (doc.isPreFork || doc.isBareFork) {
|
} else if (doc.isPreFork || doc.isBareFork) {
|
||||||
// A new unsaved document, or a fiddle, or a public example.
|
// A new unsaved document, or a fiddle, or a public example.
|
||||||
const saveActionTitle = doc.isBareFork ? t('SaveDocument') : t('SaveCopy');
|
const saveActionTitle = doc.isBareFork ? t("Save Document") : t("Save Copy");
|
||||||
return shareButton(saveActionTitle, () => [
|
return shareButton(saveActionTitle, () => [
|
||||||
menuManageUsers(doc, pageModel),
|
menuManageUsers(doc, pageModel),
|
||||||
menuSaveCopy(saveActionTitle, doc, appModel),
|
menuSaveCopy(saveActionTitle, doc, appModel),
|
||||||
@ -58,16 +58,16 @@ export function buildShareMenuButton(pageModel: DocPageModel): DomContents {
|
|||||||
// Copy" primary and keep it as an action button on top. Otherwise, show a tag without a
|
// Copy" primary and keep it as an action button on top. Otherwise, show a tag without a
|
||||||
// default action; click opens the menu where the user can choose.
|
// default action; click opens the menu where the user can choose.
|
||||||
if (!roles.canEdit(doc.trunkAccess || null)) {
|
if (!roles.canEdit(doc.trunkAccess || null)) {
|
||||||
return shareButton(t('SaveCopy'), () => [
|
return shareButton(t("Save Copy"), () => [
|
||||||
menuManageUsers(doc, pageModel),
|
menuManageUsers(doc, pageModel),
|
||||||
menuSaveCopy(t('SaveCopy'), doc, appModel),
|
menuSaveCopy(t("Save Copy"), doc, appModel),
|
||||||
menuOriginal(doc, appModel, false),
|
menuOriginal(doc, appModel, false),
|
||||||
menuExports(doc, pageModel),
|
menuExports(doc, pageModel),
|
||||||
], {buttonAction: saveCopy});
|
], {buttonAction: saveCopy});
|
||||||
} else {
|
} else {
|
||||||
return shareButton(t('Unsaved'), () => [
|
return shareButton(t("Unsaved"), () => [
|
||||||
menuManageUsers(doc, pageModel),
|
menuManageUsers(doc, pageModel),
|
||||||
menuSaveCopy(t('SaveCopy'), doc, appModel),
|
menuSaveCopy(t("Save Copy"), doc, appModel),
|
||||||
menuOriginal(doc, appModel, false),
|
menuOriginal(doc, appModel, false),
|
||||||
menuExports(doc, pageModel),
|
menuExports(doc, pageModel),
|
||||||
]);
|
]);
|
||||||
@ -75,7 +75,7 @@ export function buildShareMenuButton(pageModel: DocPageModel): DomContents {
|
|||||||
} else {
|
} else {
|
||||||
return shareButton(null, () => [
|
return shareButton(null, () => [
|
||||||
menuManageUsers(doc, pageModel),
|
menuManageUsers(doc, pageModel),
|
||||||
menuSaveCopy(t('DuplicateDocument'), doc, appModel),
|
menuSaveCopy(t("Duplicate Document"), doc, appModel),
|
||||||
menuWorkOnCopy(pageModel),
|
menuWorkOnCopy(pageModel),
|
||||||
menuExports(doc, pageModel),
|
menuExports(doc, pageModel),
|
||||||
]);
|
]);
|
||||||
@ -132,7 +132,7 @@ function shareButton(buttonText: string|null, menuCreateFunc: MenuCreateFunc,
|
|||||||
function menuManageUsers(doc: DocInfo, pageModel: DocPageModel) {
|
function menuManageUsers(doc: DocInfo, pageModel: DocPageModel) {
|
||||||
return [
|
return [
|
||||||
menuItem(() => manageUsers(doc, pageModel),
|
menuItem(() => manageUsers(doc, pageModel),
|
||||||
roles.canEditAccess(doc.access) ? t('ManageUsers') : t('AccessDetails'),
|
roles.canEditAccess(doc.access) ? t("Manage Users") : t("Access Details"),
|
||||||
dom.cls('disabled', doc.isFork),
|
dom.cls('disabled', doc.isFork),
|
||||||
testId('tb-share-option')
|
testId('tb-share-option')
|
||||||
),
|
),
|
||||||
@ -143,7 +143,7 @@ function menuManageUsers(doc: DocInfo, pageModel: DocPageModel) {
|
|||||||
// Renders "Return to Original" and "Replace Original" menu items. When used with snapshots, we
|
// Renders "Return to Original" and "Replace Original" menu items. When used with snapshots, we
|
||||||
// say "Current Version" in place of the word "Original".
|
// say "Current Version" in place of the word "Original".
|
||||||
function menuOriginal(doc: Document, appModel: AppModel, isSnapshot: boolean) {
|
function menuOriginal(doc: Document, appModel: AppModel, isSnapshot: boolean) {
|
||||||
const termToUse = isSnapshot ? t("CurrentVersion") : t("Original");
|
const termToUse = isSnapshot ? t("Current Version") : t("Original");
|
||||||
const origUrlId = buildOriginalUrlId(doc.id, isSnapshot);
|
const origUrlId = buildOriginalUrlId(doc.id, isSnapshot);
|
||||||
const originalUrl = urlState().makeUrl({doc: origUrlId});
|
const originalUrl = urlState().makeUrl({doc: origUrlId});
|
||||||
|
|
||||||
@ -166,18 +166,18 @@ function menuOriginal(doc: Document, appModel: AppModel, isSnapshot: boolean) {
|
|||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
cssMenuSplitLink({href: originalUrl},
|
cssMenuSplitLink({href: originalUrl},
|
||||||
cssMenuSplitLinkText(t('ReturnToTermToUse', {termToUse})), testId('return-to-original'),
|
cssMenuSplitLinkText(t("Return to {{termToUse}}", {termToUse})), testId('return-to-original'),
|
||||||
cssMenuIconLink({href: originalUrl, target: '_blank'}, testId('open-original'),
|
cssMenuIconLink({href: originalUrl, target: '_blank'}, testId('open-original'),
|
||||||
cssMenuIcon('FieldLink'),
|
cssMenuIcon('FieldLink'),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
menuItem(replaceOriginal, t('ReplaceTermToUse', {termToUse}),
|
menuItem(replaceOriginal, t("Replace {{termToUse}}...", {termToUse}),
|
||||||
// Disable if original is not writable, and also when comparing snapshots (since it's
|
// Disable if original is not writable, and also when comparing snapshots (since it's
|
||||||
// unclear which of the versions to use).
|
// unclear which of the versions to use).
|
||||||
dom.cls('disabled', !roles.canEdit(doc.trunkAccess || null) || comparingSnapshots),
|
dom.cls('disabled', !roles.canEdit(doc.trunkAccess || null) || comparingSnapshots),
|
||||||
testId('replace-original'),
|
testId('replace-original'),
|
||||||
),
|
),
|
||||||
menuItemLink(compareHref, {target: '_blank'}, t('CompareTermToUse', {termToUse}),
|
menuItemLink(compareHref, {target: '_blank'}, t("Compare to {{termToUse}}", {termToUse}),
|
||||||
menuAnnotate('Beta'),
|
menuAnnotate('Beta'),
|
||||||
testId('compare-original'),
|
testId('compare-original'),
|
||||||
),
|
),
|
||||||
@ -205,10 +205,10 @@ function menuWorkOnCopy(pageModel: DocPageModel) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
menuItem(makeUnsavedCopy, t('WorkOnCopy'), testId('work-on-copy')),
|
menuItem(makeUnsavedCopy, t("Work on a Copy"), testId('work-on-copy')),
|
||||||
menuText(
|
menuText(
|
||||||
withInfoTooltip(
|
withInfoTooltip(
|
||||||
t('EditWithoutAffecting'),
|
t("Edit without affecting the original"),
|
||||||
GristTooltips.workOnACopy(),
|
GristTooltips.workOnACopy(),
|
||||||
{tooltipMenuOptions: {attach: null}}
|
{tooltipMenuOptions: {attach: null}}
|
||||||
)
|
)
|
||||||
@ -229,21 +229,21 @@ function menuExports(doc: Document, pageModel: DocPageModel) {
|
|||||||
menuDivider(),
|
menuDivider(),
|
||||||
(isElectron ?
|
(isElectron ?
|
||||||
menuItem(() => gristDoc.app.comm.showItemInFolder(doc.name),
|
menuItem(() => gristDoc.app.comm.showItemInFolder(doc.name),
|
||||||
t('ShowInFolder'), testId('tb-share-option')) :
|
t("Show in folder"), testId('tb-share-option')) :
|
||||||
menuItemLink({
|
menuItemLink({
|
||||||
href: pageModel.appModel.api.getDocAPI(doc.id).getDownloadUrl(),
|
href: pageModel.appModel.api.getDocAPI(doc.id).getDownloadUrl(),
|
||||||
target: '_blank', download: ''
|
target: '_blank', download: ''
|
||||||
},
|
},
|
||||||
menuIcon('Download'), t('Download'), testId('tb-share-option'))
|
menuIcon('Download'), t("Download"), testId('tb-share-option'))
|
||||||
),
|
),
|
||||||
menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''},
|
menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''},
|
||||||
menuIcon('Download'), t('ExportCSV'), testId('tb-share-option')),
|
menuIcon('Download'), t("Export CSV"), testId('tb-share-option')),
|
||||||
menuItemLink({
|
menuItemLink({
|
||||||
href: pageModel.appModel.api.getDocAPI(doc.id).getDownloadXlsxUrl(),
|
href: pageModel.appModel.api.getDocAPI(doc.id).getDownloadXlsxUrl(),
|
||||||
target: '_blank', download: ''
|
target: '_blank', download: ''
|
||||||
}, menuIcon('Download'), t('ExportXLSX'), testId('tb-share-option')),
|
}, menuIcon('Download'), t("Export XLSX"), testId('tb-share-option')),
|
||||||
menuItem(() => sendToDrive(doc, pageModel),
|
menuItem(() => sendToDrive(doc, pageModel),
|
||||||
menuIcon('Download'), t('SendToGoogleDrive'), testId('tb-share-option')),
|
menuIcon('Download'), t("Send to Google Drive"), testId('tb-share-option')),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export function buildSiteSwitcher(appModel: AppModel) {
|
|||||||
const orgs = appModel.topAppModel.orgs;
|
const orgs = appModel.topAppModel.orgs;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
menuSubHeader(t('SwitchSites')),
|
menuSubHeader(t("Switch Sites")),
|
||||||
dom.forEach(orgs, (org) =>
|
dom.forEach(orgs, (org) =>
|
||||||
menuItemLink(urlState().setLinkUrl({ org: org.domain || undefined }),
|
menuItemLink(urlState().setLinkUrl({ org: org.domain || undefined }),
|
||||||
cssOrgSelected.cls('', appModel.currentOrg ? org.id === appModel.currentOrg.id : false),
|
cssOrgSelected.cls('', appModel.currentOrg ? org.id === appModel.currentOrg.id : false),
|
||||||
@ -45,7 +45,7 @@ export function buildSiteSwitcher(appModel: AppModel) {
|
|||||||
menuItem(
|
menuItem(
|
||||||
() => appModel.showNewSiteModal(),
|
() => appModel.showNewSiteModal(),
|
||||||
menuIcon('Plus'),
|
menuIcon('Plus'),
|
||||||
t('CreateNewTeamSite'),
|
t("Create new team site"),
|
||||||
testId('create-new-site'),
|
testId('create-new-site'),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -149,9 +149,9 @@ export class SortConfig extends Disposable {
|
|||||||
});
|
});
|
||||||
return {computed, allowedTypes, flag, label};
|
return {computed, allowedTypes, flag, label};
|
||||||
};
|
};
|
||||||
const orderByChoice = computedFlag('orderByChoice', ['Choice'], t('UseChoicePosition'));
|
const orderByChoice = computedFlag('orderByChoice', ['Choice'], t("Use choice position"));
|
||||||
const naturalSort = computedFlag('naturalSort', ['Text'], t('NaturalSort'));
|
const naturalSort = computedFlag('naturalSort', ['Text'], t("Natural sort"));
|
||||||
const emptyLast = computedFlag('emptyLast', null, t('EmptyValuesLast'));
|
const emptyLast = computedFlag('emptyLast', null, t("Empty values last"));
|
||||||
const flags = [orderByChoice, emptyLast, naturalSort];
|
const flags = [orderByChoice, emptyLast, naturalSort];
|
||||||
|
|
||||||
const column = columns.get().find(c => c.value === Sort.getColRef(colRef));
|
const column = columns.get().find(c => c.value === Sort.getColRef(colRef));
|
||||||
@ -222,7 +222,7 @@ export class SortConfig extends Disposable {
|
|||||||
dom.domComputed(use => {
|
dom.domComputed(use => {
|
||||||
const cols = use(available);
|
const cols = use(available);
|
||||||
return cssTextBtn(
|
return cssTextBtn(
|
||||||
t('AddColumn'),
|
t("Add Column"),
|
||||||
menu((ctl) => [
|
menu((ctl) => [
|
||||||
...cols.map((col) => (
|
...cols.map((col) => (
|
||||||
menuItem(
|
menuItem(
|
||||||
@ -249,7 +249,7 @@ export class SortConfig extends Disposable {
|
|||||||
private _buildUpdateDataButton() {
|
private _buildUpdateDataButton() {
|
||||||
return dom.maybe(this._section.isSorted, () =>
|
return dom.maybe(this._section.isSorted, () =>
|
||||||
cssButtonRow(
|
cssButtonRow(
|
||||||
cssTextBtn(t('UpdateData'),
|
cssTextBtn(t("Update Data"),
|
||||||
dom.on('click', () => updatePositions(this._gristDoc, this._section)),
|
dom.on('click', () => updatePositions(this._gristDoc, this._section)),
|
||||||
testId('update'),
|
testId('update'),
|
||||||
dom.show((use) => (
|
dom.show((use) => (
|
||||||
|
@ -51,7 +51,7 @@ export class SortFilterConfig extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _save() {
|
private async _save() {
|
||||||
await this._docModel.docData.bundleActions(t('UpdateSortFilterSettings'), () => Promise.all([
|
await this._docModel.docData.bundleActions(t('Update Sort & Filter settings'), () => Promise.all([
|
||||||
this._section.activeSortJson.save(),
|
this._section.activeSortJson.save(),
|
||||||
this._section.saveFilters(),
|
this._section.saveFilters(),
|
||||||
]));
|
]));
|
||||||
|
@ -26,7 +26,7 @@ export class ThemeConfig extends Disposable {
|
|||||||
|
|
||||||
public buildDom() {
|
public buildDom() {
|
||||||
return dom('div',
|
return dom('div',
|
||||||
css.subHeader(t('Appearance'), css.betaTag('Beta')),
|
css.subHeader(t("Appearance "), css.betaTag('Beta')),
|
||||||
css.dataRow(
|
css.dataRow(
|
||||||
cssAppearanceSelect(
|
cssAppearanceSelect(
|
||||||
select(
|
select(
|
||||||
@ -42,7 +42,7 @@ export class ThemeConfig extends Disposable {
|
|||||||
css.dataRow(
|
css.dataRow(
|
||||||
labeledSquareCheckbox(
|
labeledSquareCheckbox(
|
||||||
this._syncWithOS,
|
this._syncWithOS,
|
||||||
t('SyncWithOS'),
|
t("Switch appearance automatically to match system"),
|
||||||
testId('sync-with-os'),
|
testId('sync-with-os'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -30,14 +30,14 @@ export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Obse
|
|||||||
updateCanViewAccessRules();
|
updateCanViewAccessRules();
|
||||||
return cssTools(
|
return cssTools(
|
||||||
cssTools.cls('-collapsed', (use) => !use(leftPanelOpen)),
|
cssTools.cls('-collapsed', (use) => !use(leftPanelOpen)),
|
||||||
cssSectionHeader(t("Tools")),
|
cssSectionHeader(t("TOOLS")),
|
||||||
cssPageEntry(
|
cssPageEntry(
|
||||||
cssPageEntry.cls('-selected', (use) => use(gristDoc.activeViewId) === 'acl'),
|
cssPageEntry.cls('-selected', (use) => use(gristDoc.activeViewId) === 'acl'),
|
||||||
cssPageEntry.cls('-disabled', (use) => !use(canViewAccessRules)),
|
cssPageEntry.cls('-disabled', (use) => !use(canViewAccessRules)),
|
||||||
dom.domComputed(canViewAccessRules, (_canViewAccessRules) => {
|
dom.domComputed(canViewAccessRules, (_canViewAccessRules) => {
|
||||||
return cssPageLink(
|
return cssPageLink(
|
||||||
cssPageIcon('EyeShow'),
|
cssPageIcon('EyeShow'),
|
||||||
cssLinkText(t('AccessRules'),
|
cssLinkText(t("Access Rules"),
|
||||||
menuAnnotate('Beta', cssBetaTag.cls(''))
|
menuAnnotate('Beta', cssBetaTag.cls(''))
|
||||||
),
|
),
|
||||||
_canViewAccessRules ? urlState().setLinkUrl({docPage: 'acl'}) : null,
|
_canViewAccessRules ? urlState().setLinkUrl({docPage: 'acl'}) : null,
|
||||||
@ -49,25 +49,25 @@ export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Obse
|
|||||||
cssPageEntry.cls('-selected', (use) => use(gristDoc.activeViewId) === 'data'),
|
cssPageEntry.cls('-selected', (use) => use(gristDoc.activeViewId) === 'data'),
|
||||||
cssPageLink(
|
cssPageLink(
|
||||||
cssPageIcon('Database'),
|
cssPageIcon('Database'),
|
||||||
cssLinkText(t('RawData')),
|
cssLinkText(t("Raw Data")),
|
||||||
testId('raw'),
|
testId('raw'),
|
||||||
urlState().setLinkUrl({docPage: 'data'})
|
urlState().setLinkUrl({docPage: 'data'})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
cssPageEntry(
|
cssPageEntry(
|
||||||
cssPageLink(cssPageIcon('Log'), cssLinkText(t('DocumentHistory')), testId('log'),
|
cssPageLink(cssPageIcon('Log'), cssLinkText(t("Document History")), testId('log'),
|
||||||
dom.on('click', () => gristDoc.showTool('docHistory')))
|
dom.on('click', () => gristDoc.showTool('docHistory')))
|
||||||
),
|
),
|
||||||
// TODO: polish validation and add it back
|
// TODO: polish validation and add it back
|
||||||
dom.maybe((use) => use(gristDoc.app.features).validationsTool, () =>
|
dom.maybe((use) => use(gristDoc.app.features).validationsTool, () =>
|
||||||
cssPageEntry(
|
cssPageEntry(
|
||||||
cssPageLink(cssPageIcon('Validation'), cssLinkText(t('ValidateData')), testId('validate'),
|
cssPageLink(cssPageIcon('Validation'), cssLinkText(t("Validate Data")), testId('validate'),
|
||||||
dom.on('click', () => gristDoc.showTool('validations'))))
|
dom.on('click', () => gristDoc.showTool('validations'))))
|
||||||
),
|
),
|
||||||
cssPageEntry(
|
cssPageEntry(
|
||||||
cssPageEntry.cls('-selected', (use) => use(gristDoc.activeViewId) === 'code'),
|
cssPageEntry.cls('-selected', (use) => use(gristDoc.activeViewId) === 'code'),
|
||||||
cssPageLink(cssPageIcon('Code'),
|
cssPageLink(cssPageIcon('Code'),
|
||||||
cssLinkText(t('CodeView')),
|
cssLinkText(t("Code View")),
|
||||||
urlState().setLinkUrl({docPage: 'code'})
|
urlState().setLinkUrl({docPage: 'code'})
|
||||||
),
|
),
|
||||||
testId('code'),
|
testId('code'),
|
||||||
@ -77,7 +77,7 @@ export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Obse
|
|||||||
const ex = buildExamples().find(e => e.urlId === doc.urlId);
|
const ex = buildExamples().find(e => e.urlId === doc.urlId);
|
||||||
if (!ex || !ex.tutorialUrl) { return null; }
|
if (!ex || !ex.tutorialUrl) { return null; }
|
||||||
return cssPageEntry(
|
return cssPageEntry(
|
||||||
cssPageLink(cssPageIcon('Page'), cssLinkText(t('HowToTutorial')), testId('tutorial'),
|
cssPageLink(cssPageIcon('Page'), cssLinkText(t("How-to Tutorial")), testId('tutorial'),
|
||||||
{href: ex.tutorialUrl, target: '_blank'},
|
{href: ex.tutorialUrl, target: '_blank'},
|
||||||
cssExampleCardOpener(
|
cssExampleCardOpener(
|
||||||
icon('TypeDetails'),
|
icon('TypeDetails'),
|
||||||
@ -97,14 +97,14 @@ export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Obse
|
|||||||
cssSplitPageEntry(
|
cssSplitPageEntry(
|
||||||
cssPageEntryMain(
|
cssPageEntryMain(
|
||||||
cssPageLink(cssPageIcon('Page'),
|
cssPageLink(cssPageIcon('Page'),
|
||||||
cssLinkText(t('DocumentTour')),
|
cssLinkText(t("Tour of this Document")),
|
||||||
urlState().setLinkUrl({docTour: true}),
|
urlState().setLinkUrl({docTour: true}),
|
||||||
testId('doctour'),
|
testId('doctour'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
!isDocOwner ? null : cssPageEntrySmall(
|
!isDocOwner ? null : cssPageEntrySmall(
|
||||||
cssPageLink(cssPageIcon('Remove'),
|
cssPageLink(cssPageIcon('Remove'),
|
||||||
dom.on('click', () => confirmModal(t('DeleteDocumentTour'), t('Delete'), () =>
|
dom.on('click', () => confirmModal(t("Delete document tour?"), t("Delete"), () =>
|
||||||
gristDoc.docData.sendAction(['RemoveTable', 'GristDocTour']))
|
gristDoc.docData.sendAction(['RemoveTable', 'GristDocTour']))
|
||||||
),
|
),
|
||||||
testId('remove-doctour')
|
testId('remove-doctour')
|
||||||
|
@ -29,7 +29,7 @@ export function createTopBarHome(appModel: AppModel) {
|
|||||||
(appModel.isTeamSite && roles.canEditAccess(appModel.currentOrg?.access || null) ?
|
(appModel.isTeamSite && roles.canEditAccess(appModel.currentOrg?.access || null) ?
|
||||||
[
|
[
|
||||||
basicButton(
|
basicButton(
|
||||||
t('ManageTeam'),
|
t("Manage Team"),
|
||||||
dom.on('click', () => manageTeamUsersApp(appModel)),
|
dom.on('click', () => manageTeamUsersApp(appModel)),
|
||||||
testId('topbar-manage-team')
|
testId('topbar-manage-team')
|
||||||
),
|
),
|
||||||
|
@ -73,7 +73,7 @@ export function buildFormulaTriggers(owner: MultiHolder, column: ColumnRec, opti
|
|||||||
const docModel = column._table.docModel;
|
const docModel = column._table.docModel;
|
||||||
const summaryText = Computed.create(owner, use => {
|
const summaryText = Computed.create(owner, use => {
|
||||||
if (use(column.recalcWhen) === RecalcWhen.MANUAL_UPDATES) {
|
if (use(column.recalcWhen) === RecalcWhen.MANUAL_UPDATES) {
|
||||||
return t('AnyField');
|
return t("Any field");
|
||||||
}
|
}
|
||||||
const deps = decodeObject(use(column.recalcDeps)) as number[]|null;
|
const deps = decodeObject(use(column.recalcDeps)) as number[]|null;
|
||||||
if (!deps || deps.length === 0) { return ''; }
|
if (!deps || deps.length === 0) { return ''; }
|
||||||
@ -98,7 +98,7 @@ export function buildFormulaTriggers(owner: MultiHolder, column: ColumnRec, opti
|
|||||||
cssRow(
|
cssRow(
|
||||||
labeledSquareCheckbox(
|
labeledSquareCheckbox(
|
||||||
applyToNew,
|
applyToNew,
|
||||||
t('NewRecords'),
|
t("Apply to new records"),
|
||||||
dom.boolAttr('disabled', newRowsDisabled),
|
dom.boolAttr('disabled', newRowsDisabled),
|
||||||
testId('field-formula-apply-to-new'),
|
testId('field-formula-apply-to-new'),
|
||||||
),
|
),
|
||||||
@ -107,8 +107,8 @@ export function buildFormulaTriggers(owner: MultiHolder, column: ColumnRec, opti
|
|||||||
labeledSquareCheckbox(
|
labeledSquareCheckbox(
|
||||||
applyOnChanges,
|
applyOnChanges,
|
||||||
dom.text(use => use(applyOnChanges) ?
|
dom.text(use => use(applyOnChanges) ?
|
||||||
t('ChangesTo') :
|
t("Apply on changes to:") :
|
||||||
t('RecordChanges')
|
t("Apply on record changes")
|
||||||
),
|
),
|
||||||
dom.boolAttr('disabled', changesDisabled),
|
dom.boolAttr('disabled', changesDisabled),
|
||||||
testId('field-formula-apply-on-changes'),
|
testId('field-formula-apply-on-changes'),
|
||||||
@ -200,14 +200,14 @@ function buildTriggerSelectors(ctl: IOpenController, tableRec: TableRec, column:
|
|||||||
cssItemsFixed(
|
cssItemsFixed(
|
||||||
cssSelectorItem(
|
cssSelectorItem(
|
||||||
labeledSquareCheckbox(current,
|
labeledSquareCheckbox(current,
|
||||||
[t('CurrentField'), cssSelectorNote('(data cleaning)')],
|
[t("Current field "), cssSelectorNote('(data cleaning)')],
|
||||||
dom.boolAttr('disabled', allUpdates),
|
dom.boolAttr('disabled', allUpdates),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
cssSelectorItem(
|
cssSelectorItem(
|
||||||
labeledSquareCheckbox(allUpdates,
|
labeledSquareCheckbox(allUpdates,
|
||||||
[`${t('AnyField')} `, cssSelectorNote('(except formulas)')]
|
[`${t("Any field")} `, cssSelectorNote('(except formulas)')]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -224,12 +224,12 @@ function buildTriggerSelectors(ctl: IOpenController, tableRec: TableRec, column:
|
|||||||
cssItemsFixed(
|
cssItemsFixed(
|
||||||
cssSelectorFooter(
|
cssSelectorFooter(
|
||||||
dom.maybe(isChanged, () =>
|
dom.maybe(isChanged, () =>
|
||||||
primaryButton(t('OK'),
|
primaryButton(t("OK"),
|
||||||
dom.on('click', () => close(true)),
|
dom.on('click', () => close(true)),
|
||||||
testId('trigger-deps-apply')
|
testId('trigger-deps-apply')
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
basicButton(dom.text(use => use(isChanged) ? t('Cancel') : t('Close')),
|
basicButton(dom.text(use => use(isChanged) ? t("Cancel") : t("Close")),
|
||||||
dom.on('click', () => close(false)),
|
dom.on('click', () => close(false)),
|
||||||
testId('trigger-deps-cancel')
|
testId('trigger-deps-cancel')
|
||||||
),
|
),
|
||||||
|
@ -24,11 +24,11 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool
|
|||||||
|
|
||||||
const contextMenu = [
|
const contextMenu = [
|
||||||
menuItemCmd(allCommands.deleteRecords,
|
menuItemCmd(allCommands.deleteRecords,
|
||||||
t('DeleteRecord'),
|
t("Delete record"),
|
||||||
testId('section-delete-card'),
|
testId('section-delete-card'),
|
||||||
dom.cls('disabled', isReadonly || isAddRow)),
|
dom.cls('disabled', isReadonly || isAddRow)),
|
||||||
menuItemCmd(allCommands.copyLink,
|
menuItemCmd(allCommands.copyLink,
|
||||||
t('CopyAnchorLink'),
|
t("Copy anchor link"),
|
||||||
testId('section-card-link'),
|
testId('section-card-link'),
|
||||||
),
|
),
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
@ -39,30 +39,30 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool
|
|||||||
return [
|
return [
|
||||||
dom.maybe((use) => ['single'].includes(use(viewSection.parentKey)), () => contextMenu),
|
dom.maybe((use) => ['single'].includes(use(viewSection.parentKey)), () => contextMenu),
|
||||||
dom.maybe((use) => !use(viewSection.isRaw) && !isLight,
|
dom.maybe((use) => !use(viewSection.isRaw) && !isLight,
|
||||||
() => menuItemCmd(allCommands.showRawData, t('ShowRawData'), testId('show-raw-data')),
|
() => menuItemCmd(allCommands.showRawData, t("Show raw data"), testId('show-raw-data')),
|
||||||
),
|
),
|
||||||
menuItemCmd(allCommands.printSection, t('PrintWidget'), testId('print-section')),
|
menuItemCmd(allCommands.printSection, t("Print widget"), testId('print-section')),
|
||||||
menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''},
|
menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''},
|
||||||
t('DownloadCSV'), testId('download-section')),
|
t("Download as CSV"), testId('download-section')),
|
||||||
menuItemLink({ href: gristDoc.getXlsxActiveViewLink(), target: '_blank', download: ''},
|
menuItemLink({ href: gristDoc.getXlsxActiveViewLink(), target: '_blank', download: ''},
|
||||||
t('DownloadXLSX'), testId('download-section')),
|
t("Download as XLSX"), testId('download-section')),
|
||||||
dom.maybe((use) => ['detail', 'single'].includes(use(viewSection.parentKey)), () =>
|
dom.maybe((use) => ['detail', 'single'].includes(use(viewSection.parentKey)), () =>
|
||||||
menuItemCmd(allCommands.editLayout, t('EditCardLayout'),
|
menuItemCmd(allCommands.editLayout, t("Edit Card Layout"),
|
||||||
dom.cls('disabled', isReadonly))),
|
dom.cls('disabled', isReadonly))),
|
||||||
|
|
||||||
dom.maybe(!isLight, () => [
|
dom.maybe(!isLight, () => [
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
menuItemCmd(allCommands.viewTabOpen, t('WidgetOptions'), testId('widget-options')),
|
menuItemCmd(allCommands.viewTabOpen, t("Widget options"), testId('widget-options')),
|
||||||
menuItemCmd(allCommands.sortFilterTabOpen, t('AdvancedSortFilter')),
|
menuItemCmd(allCommands.sortFilterTabOpen, t("Advanced Sort & Filter")),
|
||||||
menuItemCmd(allCommands.dataSelectionTabOpen, t('DataSelection')),
|
menuItemCmd(allCommands.dataSelectionTabOpen, t("Data selection")),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
menuDivider(),
|
menuDivider(),
|
||||||
dom.maybe((use) => use(viewSection.parentKey) === 'custom' && use(viewSection.hasCustomOptions), () =>
|
dom.maybe((use) => use(viewSection.parentKey) === 'custom' && use(viewSection.hasCustomOptions), () =>
|
||||||
menuItemCmd(allCommands.openWidgetConfiguration, t('OpenConfiguration'),
|
menuItemCmd(allCommands.openWidgetConfiguration, t("Open configuration"),
|
||||||
testId('section-open-configuration')),
|
testId('section-open-configuration')),
|
||||||
),
|
),
|
||||||
menuItemCmd(allCommands.deleteSection, t('DeleteWidget'),
|
menuItemCmd(allCommands.deleteSection, t("Delete widget"),
|
||||||
dom.cls('disabled', !viewRec.getRowId() || viewRec.viewSections().peekLength <= 1 || isReadonly),
|
dom.cls('disabled', !viewRec.getRowId() || viewRec.viewSections().peekLength <= 1 || isReadonly),
|
||||||
testId('section-delete')),
|
testId('section-delete')),
|
||||||
];
|
];
|
||||||
|
@ -19,7 +19,7 @@ const t = makeT('ViewSectionMenu');
|
|||||||
|
|
||||||
// Handler for [Save] button.
|
// Handler for [Save] button.
|
||||||
async function doSave(docModel: DocModel, viewSection: ViewSectionRec): Promise<void> {
|
async function doSave(docModel: DocModel, viewSection: ViewSectionRec): Promise<void> {
|
||||||
await docModel.docData.bundleActions(t("UpdateSortFilterSettings"), () => Promise.all([
|
await docModel.docData.bundleActions(t("Update Sort&Filter settings"), () => Promise.all([
|
||||||
viewSection.activeSortJson.save(), // Save sort
|
viewSection.activeSortJson.save(), // Save sort
|
||||||
viewSection.saveFilters(), // Save filter
|
viewSection.saveFilters(), // Save filter
|
||||||
viewSection.activeCustomOptions.save(), // Save widget options
|
viewSection.activeCustomOptions.save(), // Save widget options
|
||||||
@ -73,7 +73,7 @@ export function viewSectionMenu(
|
|||||||
// [Save] [Revert] buttons when there are unsaved options.
|
// [Save] [Revert] buttons when there are unsaved options.
|
||||||
dom.maybe(displaySaveObs, () => cssSectionSaveButtonsWrapper(
|
dom.maybe(displaySaveObs, () => cssSectionSaveButtonsWrapper(
|
||||||
cssSaveTextButton(
|
cssSaveTextButton(
|
||||||
t('Save'),
|
t("Save"),
|
||||||
cssSaveTextButton.cls('-accent'),
|
cssSaveTextButton.cls('-accent'),
|
||||||
dom.on('click', save),
|
dom.on('click', save),
|
||||||
hoverTooltip('Save sort & filter settings', {key: 'sortFilterBtnTooltip'}),
|
hoverTooltip('Save sort & filter settings', {key: 'sortFilterBtnTooltip'}),
|
||||||
@ -99,10 +99,10 @@ export function viewSectionMenu(
|
|||||||
// [Save] [Revert] buttons
|
// [Save] [Revert] buttons
|
||||||
dom.domComputed(displaySaveObs, displaySave => [
|
dom.domComputed(displaySaveObs, displaySave => [
|
||||||
displaySave ? cssSaveButtonsRow(
|
displaySave ? cssSaveButtonsRow(
|
||||||
cssSaveButton(t('Save'), testId('btn-save'),
|
cssSaveButton(t("Save"), testId('btn-save'),
|
||||||
dom.on('click', () => { ctl.close(); save(); }),
|
dom.on('click', () => { ctl.close(); save(); }),
|
||||||
dom.boolAttr('disabled', isReadonly)),
|
dom.boolAttr('disabled', isReadonly)),
|
||||||
basicButton(t('Revert'), testId('btn-revert'),
|
basicButton(t("Revert"), testId('btn-revert'),
|
||||||
dom.on('click', () => { ctl.close(); revert(); }))
|
dom.on('click', () => { ctl.close(); revert(); }))
|
||||||
) : null,
|
) : null,
|
||||||
]),
|
]),
|
||||||
@ -142,7 +142,7 @@ export function viewSectionMenu(
|
|||||||
|
|
||||||
function makeSortPanel(section: ViewSectionRec, gristDoc: GristDoc) {
|
function makeSortPanel(section: ViewSectionRec, gristDoc: GristDoc) {
|
||||||
return [
|
return [
|
||||||
cssLabel(t('Sort'), testId('heading-sort')),
|
cssLabel(t("SORT"), testId('heading-sort')),
|
||||||
dom.create(SortConfig, section, gristDoc, {
|
dom.create(SortConfig, section, gristDoc, {
|
||||||
// Attach content to triggerElem's parent, which is needed to prevent view
|
// Attach content to triggerElem's parent, which is needed to prevent view
|
||||||
// section menu to close when clicking an item in the advanced sort menu.
|
// section menu to close when clicking an item in the advanced sort menu.
|
||||||
@ -153,7 +153,7 @@ function makeSortPanel(section: ViewSectionRec, gristDoc: GristDoc) {
|
|||||||
|
|
||||||
function makeFilterPanel(section: ViewSectionRec) {
|
function makeFilterPanel(section: ViewSectionRec) {
|
||||||
return [
|
return [
|
||||||
cssLabel(t('Filter'), testId('heading-filter')),
|
cssLabel(t("FILTER"), testId('heading-filter')),
|
||||||
dom.create(FilterConfig, section, {
|
dom.create(FilterConfig, section, {
|
||||||
// Attach content to triggerElem's parent, which is needed to prevent view
|
// Attach content to triggerElem's parent, which is needed to prevent view
|
||||||
// section menu to close when clicking an item of the add filter menu.
|
// section menu to close when clicking an item of the add filter menu.
|
||||||
@ -168,13 +168,13 @@ function makeCustomOptions(section: ViewSectionRec) {
|
|||||||
const color = Computed.create(null, use => use(section.activeCustomOptions.isSaved) ? "-normal" : "-accent");
|
const color = Computed.create(null, use => use(section.activeCustomOptions.isSaved) ? "-normal" : "-accent");
|
||||||
const text = Computed.create(null, use => {
|
const text = Computed.create(null, use => {
|
||||||
if (use(section.activeCustomOptions)) {
|
if (use(section.activeCustomOptions)) {
|
||||||
return use(section.activeCustomOptions.isSaved) ? t("Customized") : t("Modified");
|
return use(section.activeCustomOptions.isSaved) ? t("(customized)") : t("(modified)");
|
||||||
} else {
|
} else {
|
||||||
return t("Empty");
|
return t("(empty)");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return [
|
return [
|
||||||
cssMenuInfoHeader(t('CustomOptions'), testId('heading-widget-options')),
|
cssMenuInfoHeader(t("Custom options"), testId('heading-widget-options')),
|
||||||
cssMenuText(
|
cssMenuText(
|
||||||
dom.autoDispose(text),
|
dom.autoDispose(text),
|
||||||
dom.autoDispose(color),
|
dom.autoDispose(color),
|
||||||
|
@ -163,8 +163,8 @@ export class VisibleFieldsConfig extends Disposable {
|
|||||||
options.hiddenFields.itemCreateFunc,
|
options.hiddenFields.itemCreateFunc,
|
||||||
{
|
{
|
||||||
itemClass: cssDragRow.className,
|
itemClass: cssDragRow.className,
|
||||||
reorder() { throw new Error(t('NoReorderHiddenField')); },
|
reorder() { throw new Error(t("Hidden Fields cannot be reordered")); },
|
||||||
receive() { throw new Error(t('NoDropInHiddenField')); },
|
receive() { throw new Error(t("Cannot drop items into Hidden Fields")); },
|
||||||
remove(item: ColumnRec) {
|
remove(item: ColumnRec) {
|
||||||
// Return the column object. This value is passed to the viewFields
|
// Return the column object. This value is passed to the viewFields
|
||||||
// receive function as its respective item parameter
|
// receive function as its respective item parameter
|
||||||
@ -204,7 +204,7 @@ export class VisibleFieldsConfig extends Disposable {
|
|||||||
() => (
|
() => (
|
||||||
cssControlLabel(
|
cssControlLabel(
|
||||||
icon('Tick'),
|
icon('Tick'),
|
||||||
t('SelectAll'),
|
t("Select All"),
|
||||||
dom.on('click', () => this._setVisibleCheckboxes(fieldsDraggable, true)),
|
dom.on('click', () => this._setVisibleCheckboxes(fieldsDraggable, true)),
|
||||||
testId('visible-fields-select-all'),
|
testId('visible-fields-select-all'),
|
||||||
)
|
)
|
||||||
@ -219,7 +219,7 @@ export class VisibleFieldsConfig extends Disposable {
|
|||||||
dom.on('click', () => this._removeSelectedFields()),
|
dom.on('click', () => this._removeSelectedFields()),
|
||||||
),
|
),
|
||||||
basicButton(
|
basicButton(
|
||||||
t('Clear'),
|
t("Clear"),
|
||||||
dom.on('click', () => this._setVisibleCheckboxes(fieldsDraggable, false)),
|
dom.on('click', () => this._setVisibleCheckboxes(fieldsDraggable, false)),
|
||||||
),
|
),
|
||||||
testId('visible-batch-buttons')
|
testId('visible-batch-buttons')
|
||||||
@ -240,7 +240,7 @@ export class VisibleFieldsConfig extends Disposable {
|
|||||||
() => (
|
() => (
|
||||||
cssControlLabel(
|
cssControlLabel(
|
||||||
icon('Tick'),
|
icon('Tick'),
|
||||||
t('SelectAll'),
|
t("Select All"),
|
||||||
dom.on('click', () => this._setHiddenCheckboxes(hiddenFieldsDraggable, true)),
|
dom.on('click', () => this._setHiddenCheckboxes(hiddenFieldsDraggable, true)),
|
||||||
testId('hidden-fields-select-all'),
|
testId('hidden-fields-select-all'),
|
||||||
)
|
)
|
||||||
@ -261,7 +261,7 @@ export class VisibleFieldsConfig extends Disposable {
|
|||||||
dom.on('click', () => this._addSelectedFields()),
|
dom.on('click', () => this._addSelectedFields()),
|
||||||
),
|
),
|
||||||
basicButton(
|
basicButton(
|
||||||
t('Clear'),
|
t("Clear"),
|
||||||
dom.on('click', () => this._setHiddenCheckboxes(hiddenFieldsDraggable, false)),
|
dom.on('click', () => this._setHiddenCheckboxes(hiddenFieldsDraggable, false)),
|
||||||
),
|
),
|
||||||
testId('hidden-batch-buttons')
|
testId('hidden-batch-buttons')
|
||||||
|
@ -31,7 +31,7 @@ export function showWelcomeQuestions(userPrefsObs: Observable<UserPrefs>): boole
|
|||||||
async function onConfirm() {
|
async function onConfirm() {
|
||||||
const selected = choices.filter((c, i) => selection[i].get()).map(c => t(c.textKey));
|
const selected = choices.filter((c, i) => selection[i].get()).map(c => t(c.textKey));
|
||||||
const use_cases = ['L', ...selected]; // Format to populate a ChoiceList column
|
const use_cases = ['L', ...selected]; // Format to populate a ChoiceList column
|
||||||
const use_other = selected.includes(t('Other')) ? otherText.get() : '';
|
const use_other = selected.includes(t("Other")) ? otherText.get() : '';
|
||||||
|
|
||||||
const submitUrl = new URL(window.location.href);
|
const submitUrl = new URL(window.location.href);
|
||||||
submitUrl.pathname = '/welcome/info';
|
submitUrl.pathname = '/welcome/info';
|
||||||
@ -51,7 +51,7 @@ export function showWelcomeQuestions(userPrefsObs: Observable<UserPrefs>): boole
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: [cssLogo(), dom('div', t('WelcomeToGrist'))],
|
title: [cssLogo(), dom('div', t("Welcome to Grist!"))],
|
||||||
body: buildInfoForm(selection, otherText),
|
body: buildInfoForm(selection, otherText),
|
||||||
saveLabel: 'Start using Grist',
|
saveLabel: 'Start using Grist',
|
||||||
saveFunc: onConfirm,
|
saveFunc: onConfirm,
|
||||||
@ -65,21 +65,21 @@ export function showWelcomeQuestions(userPrefsObs: Observable<UserPrefs>): boole
|
|||||||
}
|
}
|
||||||
|
|
||||||
const choices: Array<{icon: IconName, color: string, textKey: string}> = [
|
const choices: Array<{icon: IconName, color: string, textKey: string}> = [
|
||||||
{icon: 'UseProduct', color: `${colors.lightGreen}`, textKey: 'ProductDevelopment' },
|
{icon: 'UseProduct', color: `${colors.lightGreen}`, textKey: 'Product Development' },
|
||||||
{icon: 'UseFinance', color: '#0075A2', textKey: 'FinanceAccounting' },
|
{icon: 'UseFinance', color: '#0075A2', textKey: 'Finance & Accounting' },
|
||||||
{icon: 'UseMedia', color: '#F7B32B', textKey: 'MediaProduction' },
|
{icon: 'UseMedia', color: '#F7B32B', textKey: 'Media Production' },
|
||||||
{icon: 'UseMonitor', color: '#F2545B', textKey: 'ITTechnology' },
|
{icon: 'UseMonitor', color: '#F2545B', textKey: 'IT & Technology' },
|
||||||
{icon: 'UseChart', color: '#7141F9', textKey: 'Marketing' },
|
{icon: 'UseChart', color: '#7141F9', textKey: 'Marketing' },
|
||||||
{icon: 'UseScience', color: '#231942', textKey: 'Research' },
|
{icon: 'UseScience', color: '#231942', textKey: 'Research' },
|
||||||
{icon: 'UseSales', color: '#885A5A', textKey: 'Sales' },
|
{icon: 'UseSales', color: '#885A5A', textKey: 'Sales' },
|
||||||
{icon: 'UseEducate', color: '#4A5899', textKey: 'Education' },
|
{icon: 'UseEducate', color: '#4A5899', textKey: 'Education' },
|
||||||
{icon: 'UseHr', color: '#688047', textKey: 'HRManagement' },
|
{icon: 'UseHr', color: '#688047', textKey: 'HR & Management' },
|
||||||
{icon: 'UseOther', color: '#929299', textKey: 'Other' },
|
{icon: 'UseOther', color: '#929299', textKey: 'Other' },
|
||||||
];
|
];
|
||||||
|
|
||||||
function buildInfoForm(selection: Observable<boolean>[], otherText: Observable<string>) {
|
function buildInfoForm(selection: Observable<boolean>[], otherText: Observable<string>) {
|
||||||
return [
|
return [
|
||||||
dom('span', t('WhatBringsYouToGrist')),
|
dom('span', t("What brings you to Grist? Please help us serve you better.")),
|
||||||
cssChoices(
|
cssChoices(
|
||||||
choices.map((item, i) => cssChoice(
|
choices.map((item, i) => cssChoice(
|
||||||
cssIcon(icon(item.icon), {style: `--icon-color: ${item.color}`}),
|
cssIcon(icon(item.icon), {style: `--icon-color: ${item.color}`}),
|
||||||
@ -89,7 +89,7 @@ function buildInfoForm(selection: Observable<boolean>[], otherText: Observable<s
|
|||||||
t(item.textKey) :
|
t(item.textKey) :
|
||||||
[
|
[
|
||||||
cssOtherLabel(t(item.textKey)),
|
cssOtherLabel(t(item.textKey)),
|
||||||
cssOtherInput(otherText, {}, {type: 'text', placeholder: t('TypeHere')},
|
cssOtherInput(otherText, {}, {type: 'text', placeholder: t("Type here")},
|
||||||
// The following subscribes to changes to selection observable, and focuses the input when
|
// The following subscribes to changes to selection observable, and focuses the input when
|
||||||
// this item is selected.
|
// this item is selected.
|
||||||
(elem) => subscribeElem(elem, selection[i], val => val && setTimeout(() => elem.focus(), 0)),
|
(elem) => subscribeElem(elem, selection[i], val => val && setTimeout(() => elem.focus(), 0)),
|
||||||
|
@ -67,7 +67,7 @@ function buildWidgetRenamePopup(ctrl: IOpenController, vs: ViewSectionRec, optio
|
|||||||
// Placeholder for widget title:
|
// Placeholder for widget title:
|
||||||
// - when widget title is empty shows a default widget title (what would be shown when title is empty)
|
// - when widget title is empty shows a default widget title (what would be shown when title is empty)
|
||||||
// - when widget title is set, shows just a text to override it.
|
// - when widget title is set, shows just a text to override it.
|
||||||
const inputWidgetPlaceholder = !vs.title.peek() ? t('OverrideTitle') : vs.defaultWidgetTitle.peek();
|
const inputWidgetPlaceholder = !vs.title.peek() ? t("Override widget title") : vs.defaultWidgetTitle.peek();
|
||||||
|
|
||||||
const disableSave = Computed.create(ctrl, (use) => {
|
const disableSave = Computed.create(ctrl, (use) => {
|
||||||
const newTableName = use(inputTableName)?.trim() ?? '';
|
const newTableName = use(inputTableName)?.trim() ?? '';
|
||||||
@ -137,29 +137,29 @@ function buildWidgetRenamePopup(ctrl: IOpenController, vs: ViewSectionRec, optio
|
|||||||
testId('popup'),
|
testId('popup'),
|
||||||
dom.cls(menuCssClass),
|
dom.cls(menuCssClass),
|
||||||
dom.maybe(!options.tableNameHidden, () => [
|
dom.maybe(!options.tableNameHidden, () => [
|
||||||
cssLabel(t('DataTableName')),
|
cssLabel(t("DATA TABLE NAME")),
|
||||||
// Update tableName on key stroke - this will show the default widget name as we type.
|
// Update tableName on key stroke - this will show the default widget name as we type.
|
||||||
// above this modal.
|
// above this modal.
|
||||||
tableInput = cssInput(
|
tableInput = cssInput(
|
||||||
inputTableName,
|
inputTableName,
|
||||||
updateOnKey,
|
updateOnKey,
|
||||||
{disabled: isSummary, placeholder: t('NewTableName')},
|
{disabled: isSummary, placeholder: t("Provide a table name")},
|
||||||
testId('table-name-input')
|
testId('table-name-input')
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
dom.maybe(!options.widgetNameHidden, () => [
|
dom.maybe(!options.widgetNameHidden, () => [
|
||||||
cssLabel(t('WidgetTitle')),
|
cssLabel(t("WIDGET TITLE")),
|
||||||
widgetInput = cssInput(inputWidgetTitle, updateOnKey, {placeholder: inputWidgetPlaceholder},
|
widgetInput = cssInput(inputWidgetTitle, updateOnKey, {placeholder: inputWidgetPlaceholder},
|
||||||
testId('section-name-input')
|
testId('section-name-input')
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
cssButtons(
|
cssButtons(
|
||||||
primaryButton(t('Save'),
|
primaryButton(t("Save"),
|
||||||
dom.on('click', doSave),
|
dom.on('click', doSave),
|
||||||
dom.boolAttr('disabled', use => use(disableSave) || use(modalCtl.workInProgress)),
|
dom.boolAttr('disabled', use => use(disableSave) || use(modalCtl.workInProgress)),
|
||||||
testId('save'),
|
testId('save'),
|
||||||
),
|
),
|
||||||
basicButton(t('Cancel'),
|
basicButton(t("Cancel"),
|
||||||
testId('cancel'),
|
testId('cancel'),
|
||||||
dom.on('click', () => modalCtl.close())
|
dom.on('click', () => modalCtl.close())
|
||||||
),
|
),
|
||||||
|
@ -28,24 +28,24 @@ export function createErrPage(appModel: AppModel) {
|
|||||||
* Creates a page to show that the user has no access to this org.
|
* Creates a page to show that the user has no access to this org.
|
||||||
*/
|
*/
|
||||||
export function createForbiddenPage(appModel: AppModel, message?: string) {
|
export function createForbiddenPage(appModel: AppModel, message?: string) {
|
||||||
document.title = t('AccessDenied', {suffix: getPageTitleSuffix(getGristConfig())});
|
document.title = t("Access denied{{suffix}}", {suffix: getPageTitleSuffix(getGristConfig())});
|
||||||
|
|
||||||
const isAnonym = () => !appModel.currentValidUser;
|
const isAnonym = () => !appModel.currentValidUser;
|
||||||
const isExternal = () => appModel.currentValidUser?.loginMethod === 'External';
|
const isExternal = () => appModel.currentValidUser?.loginMethod === 'External';
|
||||||
return pagePanelsError(appModel, t('AccessDenied', {suffix: ''}), [
|
return pagePanelsError(appModel, t("Access denied{{suffix}}", {suffix: ''}), [
|
||||||
dom.domComputed(appModel.currentValidUser, user => user ? [
|
dom.domComputed(appModel.currentValidUser, user => user ? [
|
||||||
cssErrorText(message || t("DeniedOrganizationDocuments")),
|
cssErrorText(message || t("You do not have access to this organization's documents.")),
|
||||||
cssErrorText(t("SignInWithDifferentAccount", {email: dom('b', user.email)})), // TODO: i18next
|
cssErrorText(t("You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.", {email: dom('b', user.email)})), // TODO: i18next
|
||||||
] : [
|
] : [
|
||||||
// This page is not normally shown because a logged out user with no access will get
|
// This page is not normally shown because a logged out user with no access will get
|
||||||
// redirected to log in. But it may be seen if a user logs out and returns to a cached
|
// redirected to log in. But it may be seen if a user logs out and returns to a cached
|
||||||
// version of this page or is an external user (connected through GristConnect).
|
// version of this page or is an external user (connected through GristConnect).
|
||||||
cssErrorText(t("SignInToAccess")),
|
cssErrorText(t("Sign in to access this organization's documents.")),
|
||||||
]),
|
]),
|
||||||
cssButtonWrap(bigPrimaryButtonLink(
|
cssButtonWrap(bigPrimaryButtonLink(
|
||||||
isExternal() ? t("GoToMainPage") :
|
isExternal() ? t("Go to main page") :
|
||||||
isAnonym() ? t("SignIn") :
|
isAnonym() ? t("Sign in") :
|
||||||
t("AddAcount"),
|
t("Add account"),
|
||||||
{href: isExternal() ? getMainOrgUrl() : getLoginUrl()},
|
{href: isExternal() ? getMainOrgUrl() : getLoginUrl()},
|
||||||
testId('error-signin'),
|
testId('error-signin'),
|
||||||
))
|
))
|
||||||
@ -56,12 +56,12 @@ export function createForbiddenPage(appModel: AppModel, message?: string) {
|
|||||||
* Creates a page that shows the user is logged out.
|
* Creates a page that shows the user is logged out.
|
||||||
*/
|
*/
|
||||||
export function createSignedOutPage(appModel: AppModel) {
|
export function createSignedOutPage(appModel: AppModel) {
|
||||||
document.title = t('SignedOut', {suffix: getPageTitleSuffix(getGristConfig())});
|
document.title = t("Signed out{{suffix}}", {suffix: getPageTitleSuffix(getGristConfig())});
|
||||||
|
|
||||||
return pagePanelsError(appModel, t('SignedOut', {suffix: ''}), [
|
return pagePanelsError(appModel, t("Signed out{{suffix}}", {suffix: ''}), [
|
||||||
cssErrorText(t('SignedOutNow')),
|
cssErrorText(t("You are now signed out.")),
|
||||||
cssButtonWrap(bigPrimaryButtonLink(
|
cssButtonWrap(bigPrimaryButtonLink(
|
||||||
t('SignedInAgain'), {href: getLoginUrl()}, testId('error-signin')
|
t("Sign in again"), {href: getLoginUrl()}, testId('error-signin')
|
||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -70,13 +70,13 @@ export function createSignedOutPage(appModel: AppModel) {
|
|||||||
* Creates a "Page not found" page.
|
* Creates a "Page not found" page.
|
||||||
*/
|
*/
|
||||||
export function createNotFoundPage(appModel: AppModel, message?: string) {
|
export function createNotFoundPage(appModel: AppModel, message?: string) {
|
||||||
document.title = t('PageNotFound', {suffix: getPageTitleSuffix(getGristConfig())});
|
document.title = t("Page not found{{suffix}}", {suffix: getPageTitleSuffix(getGristConfig())});
|
||||||
|
|
||||||
return pagePanelsError(appModel, t('PageNotFound', {suffix: ''}), [
|
return pagePanelsError(appModel, t("Page not found{{suffix}}", {suffix: ''}), [
|
||||||
cssErrorText(message || t('NotFoundMainText', {separator: dom('br')})), // TODO: i18next
|
cssErrorText(message || t("The requested page could not be found.{{separator}}Please check the URL and try again.", {separator: dom('br')})), // TODO: i18next
|
||||||
cssButtonWrap(bigPrimaryButtonLink(t('GoToMainPage'), testId('error-primary-btn'),
|
cssButtonWrap(bigPrimaryButtonLink(t("Go to main page"), testId('error-primary-btn'),
|
||||||
urlState().setLinkUrl({}))),
|
urlState().setLinkUrl({}))),
|
||||||
cssButtonWrap(bigBasicButtonLink(t('ContactSupport'), {href: 'https://getgrist.com/contact'})),
|
cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: 'https://getgrist.com/contact'})),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,14 +84,14 @@ export function createNotFoundPage(appModel: AppModel, message?: string) {
|
|||||||
* Creates a generic error page with the given message.
|
* Creates a generic error page with the given message.
|
||||||
*/
|
*/
|
||||||
export function createOtherErrorPage(appModel: AppModel, message?: string) {
|
export function createOtherErrorPage(appModel: AppModel, message?: string) {
|
||||||
document.title = t('GenericError', {suffix: getPageTitleSuffix(getGristConfig())});
|
document.title = t("Error{{suffix}}", {suffix: getPageTitleSuffix(getGristConfig())});
|
||||||
|
|
||||||
return pagePanelsError(appModel, t('SomethingWentWrong'), [
|
return pagePanelsError(appModel, t("Something went wrong"), [
|
||||||
cssErrorText(message ? t('ErrorHappened', {context: 'message', message: addPeriod(message)}) :
|
cssErrorText(message ? t('There was an error: {{message}}', {message: addPeriod(message)}) :
|
||||||
t('ErrorHappened', {context: 'unknown'})),
|
t('There was an unknown error.')),
|
||||||
cssButtonWrap(bigPrimaryButtonLink(t('GoToMainPage'), testId('error-primary-btn'),
|
cssButtonWrap(bigPrimaryButtonLink(t("Go to main page"), testId('error-primary-btn'),
|
||||||
urlState().setLinkUrl({}))),
|
urlState().setLinkUrl({}))),
|
||||||
cssButtonWrap(bigBasicButtonLink(t('ContactSupport'), {href: 'https://getgrist.com/contact'})),
|
cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: 'https://getgrist.com/contact'})),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export async function sendToDrive(doc: Document, pageModel: DocPageModel) {
|
|||||||
// Create send to google drive handler (it will return a spreadsheet url).
|
// Create send to google drive handler (it will return a spreadsheet url).
|
||||||
const send = (code: string) =>
|
const send = (code: string) =>
|
||||||
// Decorate it with a spinner
|
// Decorate it with a spinner
|
||||||
spinnerModal(t('SendingToGoogleDrive'),
|
spinnerModal(t("Sending file to Google Drive"),
|
||||||
pageModel.appModel.api.getDocAPI(doc.id)
|
pageModel.appModel.api.getDocAPI(doc.id)
|
||||||
.sendToDrive(code, pageModel.currentDocTitle.get())
|
.sendToDrive(code, pageModel.currentDocTitle.get())
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,7 @@ import {BindableValue, Computed, Disposable, dom, Observable, onKeyDown, styled}
|
|||||||
import {defaultMenuOptions, IOpenController, setPopupToCreateDom} from 'popweasel';
|
import {defaultMenuOptions, IOpenController, setPopupToCreateDom} from 'popweasel';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
|
|
||||||
const t = makeT('ui2018.ColorSelect');
|
const t = makeT('ColorSelect');
|
||||||
|
|
||||||
export interface StyleOptions {
|
export interface StyleOptions {
|
||||||
textColor: ColorOption,
|
textColor: ColorOption,
|
||||||
@ -64,7 +64,7 @@ export function colorSelect(
|
|||||||
onSave,
|
onSave,
|
||||||
onOpen,
|
onOpen,
|
||||||
onRevert,
|
onRevert,
|
||||||
placeholder = t('DefaultCellStyle'),
|
placeholder = t("Default cell style"),
|
||||||
} = options;
|
} = options;
|
||||||
const selectBtn = cssSelectBtn(
|
const selectBtn = cssSelectBtn(
|
||||||
cssContent(
|
cssContent(
|
||||||
@ -188,12 +188,12 @@ function buildColorPicker(ctl: IOpenController,
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
cssButtonRow(
|
cssButtonRow(
|
||||||
primaryButton(t('Apply'),
|
primaryButton(t("Apply"),
|
||||||
dom.on('click', () => ctl.close()),
|
dom.on('click', () => ctl.close()),
|
||||||
dom.boolAttr("disabled", notChanged),
|
dom.boolAttr("disabled", notChanged),
|
||||||
testId('colors-save')
|
testId('colors-save')
|
||||||
),
|
),
|
||||||
basicButton(t('Cancel'),
|
basicButton(t("Cancel"),
|
||||||
dom.on('click', () => revert()),
|
dom.on('click', () => revert()),
|
||||||
testId('colors-cancel')
|
testId('colors-cancel')
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ import { cssLink } from 'app/client/ui2018/links';
|
|||||||
import { BindableValue, dom, Observable, styled } from 'grainjs';
|
import { BindableValue, dom, Observable, styled } from 'grainjs';
|
||||||
import { tooltip } from 'popweasel';
|
import { tooltip } from 'popweasel';
|
||||||
|
|
||||||
const t = makeT('ui2018.breadcrumbs');
|
const t = makeT('breadcrumbs');
|
||||||
|
|
||||||
export const cssBreadcrumbs = styled('div', `
|
export const cssBreadcrumbs = styled('div', `
|
||||||
color: ${theme.lightText};
|
color: ${theme.lightText};
|
||||||
@ -138,19 +138,20 @@ export function docBreadcrumbs(
|
|||||||
dom.maybe(options.isPublic, () => cssPublicIcon('PublicFilled', testId('bc-is-public'))),
|
dom.maybe(options.isPublic, () => cssPublicIcon('PublicFilled', testId('bc-is-public'))),
|
||||||
dom.domComputed((use) => {
|
dom.domComputed((use) => {
|
||||||
if (options.isSnapshot && use(options.isSnapshot)) {
|
if (options.isSnapshot && use(options.isSnapshot)) {
|
||||||
return cssTag(t('Snapshot'), testId('snapshot-tag'));
|
return cssTag(t("snapshot"), testId('snapshot-tag'));
|
||||||
}
|
}
|
||||||
if (use(options.isFork)) {
|
if (use(options.isFork)) {
|
||||||
return cssTag(t('Unsaved'), testId('unsaved-tag'));
|
return cssTag(t("unsaved"), testId('unsaved-tag'));
|
||||||
}
|
}
|
||||||
if (use(options.isRecoveryMode)) {
|
if (use(options.isRecoveryMode)) {
|
||||||
return cssAlertTag(t('RecoveryMode'),
|
return cssAlertTag(t("recovery mode"),
|
||||||
dom('a', dom.on('click', () => options.cancelRecoveryMode()),
|
dom('a', dom.on('click', () => options.cancelRecoveryMode()),
|
||||||
icon('CrossSmall')),
|
icon('CrossSmall')),
|
||||||
testId('recovery-mode-tag'));
|
testId('recovery-mode-tag'));
|
||||||
}
|
}
|
||||||
if (use(options.isFiddle)) {
|
if (use(options.isFiddle)) {
|
||||||
return cssTag(t('Fiddle'), tooltip({title: t('FiddleExplanation')}), testId('fiddle-tag'));
|
return cssTag(t("fiddle"), tooltip({title: t(`You may make edits, but they will create a new copy and will
|
||||||
|
not affect the original document.`)}), testId('fiddle-tag'));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
separator(' / ',
|
separator(' / ',
|
||||||
|
@ -11,7 +11,7 @@ import { BindableValue, Computed, dom, DomElementArg, DomElementMethod, IDomArgs
|
|||||||
MaybeObsArray, MutableObsArray, Observable, styled } from 'grainjs';
|
MaybeObsArray, MutableObsArray, Observable, styled } from 'grainjs';
|
||||||
import * as weasel from 'popweasel';
|
import * as weasel from 'popweasel';
|
||||||
|
|
||||||
const t = makeT('ui2018.menus');
|
const t = makeT('menus');
|
||||||
|
|
||||||
export interface IOptionFull<T> {
|
export interface IOptionFull<T> {
|
||||||
value: T;
|
value: T;
|
||||||
@ -189,7 +189,7 @@ export function multiSelect<T>(selectedOptions: MutableObsArray<T>,
|
|||||||
|
|
||||||
const selectedOptionsText = Computed.create(null, selectedOptionsSet, (use, selectedOpts) => {
|
const selectedOptionsText = Computed.create(null, selectedOptionsSet, (use, selectedOpts) => {
|
||||||
if (selectedOpts.size === 0) {
|
if (selectedOpts.size === 0) {
|
||||||
return options.placeholder ?? t('SelectFields');
|
return options.placeholder ?? t("Select fields");
|
||||||
}
|
}
|
||||||
|
|
||||||
const optionArray = Array.isArray(availableOptions) ? availableOptions : use(availableOptions);
|
const optionArray = Array.isArray(availableOptions) ? availableOptions : use(availableOptions);
|
||||||
@ -323,8 +323,8 @@ export function upgradableMenuItem(needUpgrade: boolean, action: () => void, ...
|
|||||||
|
|
||||||
export function upgradeText(needUpgrade: boolean, onClick: () => void) {
|
export function upgradeText(needUpgrade: boolean, onClick: () => void) {
|
||||||
if (!needUpgrade) { return null; }
|
if (!needUpgrade) { return null; }
|
||||||
return menuText(dom('span', t('WorkspacesAvailableOnTeamPlans'),
|
return menuText(dom('span', t("* Workspaces are available on team plans. "),
|
||||||
cssUpgradeTextButton(t('UpgradeNow'), dom.on('click', () => onClick()))));
|
cssUpgradeTextButton(t("Upgrade now"), dom.on('click', () => onClick()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,7 @@ import {Computed, Disposable, dom, DomContents, DomElementArg, input, keyframes,
|
|||||||
MultiHolder, Observable, styled} from 'grainjs';
|
MultiHolder, Observable, styled} from 'grainjs';
|
||||||
import {cssMenuElem} from 'app/client/ui2018/menus';
|
import {cssMenuElem} from 'app/client/ui2018/menus';
|
||||||
|
|
||||||
const t = makeT('ui2018.modals');
|
const t = makeT('modals');
|
||||||
|
|
||||||
// IModalControl is passed into the function creating the body of the modal.
|
// IModalControl is passed into the function creating the body of the modal.
|
||||||
export interface IModalControl {
|
export interface IModalControl {
|
||||||
@ -306,13 +306,13 @@ export function saveModal(createFunc: (ctl: IModalControl, owner: MultiHolder) =
|
|||||||
cssModalTitle(options.title, testId('modal-title')),
|
cssModalTitle(options.title, testId('modal-title')),
|
||||||
cssModalBody(options.body),
|
cssModalBody(options.body),
|
||||||
cssModalButtons(
|
cssModalButtons(
|
||||||
bigPrimaryButton(options.saveLabel || t('Save'),
|
bigPrimaryButton(options.saveLabel || t("Save"),
|
||||||
dom.boolAttr('disabled', isSaveDisabled),
|
dom.boolAttr('disabled', isSaveDisabled),
|
||||||
dom.on('click', save),
|
dom.on('click', save),
|
||||||
testId('modal-confirm'),
|
testId('modal-confirm'),
|
||||||
),
|
),
|
||||||
options.extraButtons,
|
options.extraButtons,
|
||||||
options.hideCancel ? null : bigBasicButton(t('Cancel'),
|
options.hideCancel ? null : bigBasicButton(t("Cancel"),
|
||||||
dom.on('click', () => ctl.close()),
|
dom.on('click', () => ctl.close()),
|
||||||
testId('modal-cancel'),
|
testId('modal-cancel'),
|
||||||
),
|
),
|
||||||
@ -426,7 +426,7 @@ export function invokePrompt(
|
|||||||
const prom = new Promise<string|undefined>((resolve) => {
|
const prom = new Promise<string|undefined>((resolve) => {
|
||||||
onResolve = resolve;
|
onResolve = resolve;
|
||||||
});
|
});
|
||||||
promptModal(title, onResolve!, btnText ?? t('Ok'), initial, placeholder, () => {
|
promptModal(title, onResolve!, btnText ?? t("Ok"), initial, placeholder, () => {
|
||||||
if (onResolve) {
|
if (onResolve) {
|
||||||
onResolve(undefined);
|
onResolve(undefined);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import { hoverTooltip } from 'app/client/ui/tooltips';
|
|||||||
import { menu, menuItem, menuText } from "app/client/ui2018/menus";
|
import { menu, menuItem, menuText } from "app/client/ui2018/menus";
|
||||||
import { dom, domComputed, DomElementArg, makeTestId, observable, Observable, styled } from "grainjs";
|
import { dom, domComputed, DomElementArg, makeTestId, observable, Observable, styled } from "grainjs";
|
||||||
|
|
||||||
const t = makeT('ui2018.pages');
|
const t = makeT('pages');
|
||||||
|
|
||||||
const testId = makeTestId('test-docpage-');
|
const testId = makeTestId('test-docpage-');
|
||||||
|
|
||||||
@ -37,11 +37,11 @@ export function buildPageDom(name: Observable<string>, actions: PageActions, ...
|
|||||||
const pageMenu = () => [
|
const pageMenu = () => [
|
||||||
menuItem(() => isRenaming.set(true), t("Rename"), testId('rename'),
|
menuItem(() => isRenaming.set(true), t("Rename"), testId('rename'),
|
||||||
dom.cls('disabled', actions.isReadonly)),
|
dom.cls('disabled', actions.isReadonly)),
|
||||||
menuItem(actions.onRemove, t('Remove'), testId('remove'),
|
menuItem(actions.onRemove, t("Remove"), testId('remove'),
|
||||||
dom.cls('disabled', (use) => use(actions.isReadonly) || actions.isRemoveDisabled())),
|
dom.cls('disabled', (use) => use(actions.isReadonly) || actions.isRemoveDisabled())),
|
||||||
menuItem(actions.onDuplicate, t('DuplicatePage'), testId('duplicate'),
|
menuItem(actions.onDuplicate, t("Duplicate Page"), testId('duplicate'),
|
||||||
dom.cls('disabled', actions.isReadonly)),
|
dom.cls('disabled', actions.isReadonly)),
|
||||||
dom.maybe(actions.isReadonly, () => menuText(t('NoEditAccess'))),
|
dom.maybe(actions.isReadonly, () => menuText(t("You do not have edit access to this document"))),
|
||||||
];
|
];
|
||||||
let pageElem: HTMLElement;
|
let pageElem: HTMLElement;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import debounce = require('lodash/debounce');
|
|||||||
|
|
||||||
export * from 'app/client/models/SearchModel';
|
export * from 'app/client/models/SearchModel';
|
||||||
|
|
||||||
const t = makeT('ui2018.search');
|
const t = makeT('search');
|
||||||
|
|
||||||
const EXPAND_TIME = .5;
|
const EXPAND_TIME = .5;
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
|
|||||||
model.isOpen.set(_value === undefined ? !model.isOpen.get() : _value);
|
model.isOpen.set(_value === undefined ? !model.isOpen.get() : _value);
|
||||||
}, 100);
|
}, 100);
|
||||||
const inputElem: HTMLInputElement = searchInput(model.value, {onInput: true},
|
const inputElem: HTMLInputElement = searchInput(model.value, {onInput: true},
|
||||||
{type: 'text', placeholder: t('SearchInDocument')},
|
{type: 'text', placeholder: t("Search in document")},
|
||||||
dom.on('blur', () => (
|
dom.on('blur', () => (
|
||||||
keepExpanded ?
|
keepExpanded ?
|
||||||
setTimeout(() => inputElem.focus(), 0) :
|
setTimeout(() => inputElem.focus(), 0) :
|
||||||
@ -187,7 +187,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
|
|||||||
const noMatch = use(model.noMatch);
|
const noMatch = use(model.noMatch);
|
||||||
const isEmpty = use(model.isEmpty);
|
const isEmpty = use(model.isEmpty);
|
||||||
if (isEmpty) { return null; }
|
if (isEmpty) { return null; }
|
||||||
if (noMatch) { return cssLabel(t("NoResults")); }
|
if (noMatch) { return cssLabel(t("No results")); }
|
||||||
return [
|
return [
|
||||||
cssArrowBtn(
|
cssArrowBtn(
|
||||||
icon('Dropdown'),
|
icon('Dropdown'),
|
||||||
@ -197,7 +197,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
|
|||||||
dom.on('click', () => model.findNext()),
|
dom.on('click', () => model.findNext()),
|
||||||
hoverTooltip(
|
hoverTooltip(
|
||||||
[
|
[
|
||||||
t('FindNext'),
|
t("Find Next "),
|
||||||
cssShortcut(`(${['Enter', allCommands.findNext.humanKeys].join(', ')})`),
|
cssShortcut(`(${['Enter', allCommands.findNext.humanKeys].join(', ')})`),
|
||||||
],
|
],
|
||||||
{key: 'searchArrowBtnTooltip'}
|
{key: 'searchArrowBtnTooltip'}
|
||||||
@ -211,7 +211,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
|
|||||||
dom.on('click', () => model.findPrev()),
|
dom.on('click', () => model.findPrev()),
|
||||||
hoverTooltip(
|
hoverTooltip(
|
||||||
[
|
[
|
||||||
t('FindPrevious'),
|
t("Find Previous "),
|
||||||
cssShortcut(allCommands.findPrev.getKeysDesc()),
|
cssShortcut(allCommands.findPrev.getKeysDesc()),
|
||||||
],
|
],
|
||||||
{key: 'searchArrowBtnTooltip'}
|
{key: 'searchArrowBtnTooltip'}
|
||||||
|
72
buildtools/generate_translation_keys.js
Normal file
72
buildtools/generate_translation_keys.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Generating translations keys:
|
||||||
|
*
|
||||||
|
* This code walk through all the files in client directory and its children
|
||||||
|
* Get the all keys called by our makeT utils function
|
||||||
|
* And add only the new one on our en.client.json file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const Parser = require("i18next-scanner").Parser;
|
||||||
|
const englishKeys = require("../static/locales/en.client.json");
|
||||||
|
const _ = require("lodash");
|
||||||
|
|
||||||
|
const parser = new Parser({
|
||||||
|
keySeparator: "/",
|
||||||
|
nsSeparator: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function* walk(dir) {
|
||||||
|
for await (const d of await fs.promises.opendir(dir)) {
|
||||||
|
const entry = path.join(dir, d.name);
|
||||||
|
if (d.isDirectory()) yield* walk(entry);
|
||||||
|
else if (d.isFile()) yield entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const customHandler = (fileName) => (key, options) => {
|
||||||
|
const keyWithFile = `${fileName}/${key}`;
|
||||||
|
if (Object.keys(options).includes("count") === true) {
|
||||||
|
const keyOne = `${keyWithFile}_one`;
|
||||||
|
const keyOther = `${keyWithFile}_other`;
|
||||||
|
parser.set(keyOne, key);
|
||||||
|
parser.set(keyOther, key);
|
||||||
|
} else {
|
||||||
|
parser.set(keyWithFile, key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getKeysFromFile = (filePath, fileName) => {
|
||||||
|
const content = fs.readFileSync(filePath, "utf-8");
|
||||||
|
parser.parseFuncFromString(
|
||||||
|
content,
|
||||||
|
{
|
||||||
|
list: [
|
||||||
|
"i18next.t",
|
||||||
|
"t", // To match the file-level t function created with makeT
|
||||||
|
],
|
||||||
|
},
|
||||||
|
customHandler(fileName)
|
||||||
|
);
|
||||||
|
const keys = parser.get({ sort: true });
|
||||||
|
return keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function walkTranslation(dirPath) {
|
||||||
|
for await (const p of walk(dirPath)) {
|
||||||
|
const { name } = path.parse(p);
|
||||||
|
getKeysFromFile(p, name);
|
||||||
|
}
|
||||||
|
const keys = parser.get({ sort: true });
|
||||||
|
const newTranslations = _.merge(keys.en.translation, englishKeys);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
"static/locales/en.client.json",
|
||||||
|
JSON.stringify(newTranslations, null, 2),
|
||||||
|
"utf-8"
|
||||||
|
);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
walkTranslation("app/client");
|
@ -20,7 +20,8 @@
|
|||||||
"test:smoke": "NODE_PATH=_build:_build/stubs:_build/ext mocha _build/test/nbrowser/Smoke.js",
|
"test:smoke": "NODE_PATH=_build:_build/stubs:_build/ext mocha _build/test/nbrowser/Smoke.js",
|
||||||
"test:docker": "./test/test_under_docker.sh",
|
"test:docker": "./test/test_under_docker.sh",
|
||||||
"test:python": "sandbox_venv3/bin/python sandbox/grist/runtests.py ${GREP_TESTS:+discover -p \"test*${GREP_TESTS}*.py\"}",
|
"test:python": "sandbox_venv3/bin/python sandbox/grist/runtests.py ${GREP_TESTS:+discover -p \"test*${GREP_TESTS}*.py\"}",
|
||||||
"cli": "NODE_PATH=_build:_build/stubs:_build/ext node _build/app/server/companion.js"
|
"cli": "NODE_PATH=_build:_build/stubs:_build/ext node _build/app/server/companion.js",
|
||||||
|
"generate:translation": "NODE_PATH=_build:_build/stubs:_build/ext node buildtools/generate_translation_keys.js"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grist",
|
"grist",
|
||||||
@ -77,6 +78,7 @@
|
|||||||
"chance": "1.0.16",
|
"chance": "1.0.16",
|
||||||
"esbuild-loader": "2.19.0",
|
"esbuild-loader": "2.19.0",
|
||||||
"http-proxy": "1.18.1",
|
"http-proxy": "1.18.1",
|
||||||
|
"i18next-scanner": "4.1.0",
|
||||||
"jsdom": "16.5.0",
|
"jsdom": "16.5.0",
|
||||||
"mocha": "5.2.0",
|
"mocha": "5.2.0",
|
||||||
"mocha-webdriver": "0.2.9",
|
"mocha-webdriver": "0.2.9",
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -112,7 +112,7 @@ describe("Localization", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("loads correct languages from file system", async function() {
|
it("loads correct languages from file system", async function() {
|
||||||
modifyByCode(tempLocale, "en", {HomeIntro: {Welcome: 'TestMessage'}});
|
modifyByCode(tempLocale, "en", {HomeIntro: {'Welcome to Grist!': 'TestMessage'}});
|
||||||
await driver.navigate().refresh();
|
await driver.navigate().refresh();
|
||||||
assert.equal(await driver.findWait('.test-welcome-title', 3000).getText(), 'TestMessage');
|
assert.equal(await driver.findWait('.test-welcome-title', 3000).getText(), 'TestMessage');
|
||||||
const gristConfig: any = await driver.executeScript("return window.gristConfig");
|
const gristConfig: any = await driver.executeScript("return window.gristConfig");
|
||||||
|
538
yarn.lock
538
yarn.lock
@ -9,6 +9,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.20.6":
|
||||||
|
version "7.20.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
|
||||||
|
integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
"@discoveryjs/json-ext@^0.5.0":
|
"@discoveryjs/json-ext@^0.5.0":
|
||||||
version "0.5.7"
|
version "0.5.7"
|
||||||
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
|
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
|
||||||
@ -820,6 +827,18 @@ accepts@~1.3.5:
|
|||||||
mime-types "~2.1.24"
|
mime-types "~2.1.24"
|
||||||
negotiator "0.6.2"
|
negotiator "0.6.2"
|
||||||
|
|
||||||
|
acorn-class-fields@^0.3.7:
|
||||||
|
version "0.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-class-fields/-/acorn-class-fields-0.3.7.tgz#a35122f3cc6ad2bb33b1857e79215677fcfdd720"
|
||||||
|
integrity sha512-jdUWSFce0fuADUljmExz4TWpPkxmRW/ZCPRqeeUzbGf0vFUcpQYbyq52l75qGd0oSwwtAepeL6hgb/naRgvcKQ==
|
||||||
|
dependencies:
|
||||||
|
acorn-private-class-elements "^0.2.7"
|
||||||
|
|
||||||
|
acorn-dynamic-import@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948"
|
||||||
|
integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==
|
||||||
|
|
||||||
acorn-globals@^6.0.0:
|
acorn-globals@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz"
|
resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz"
|
||||||
@ -833,6 +852,11 @@ acorn-import-assertions@^1.7.6:
|
|||||||
resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz"
|
resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz"
|
||||||
integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
|
integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
|
||||||
|
|
||||||
|
acorn-jsx@^5.3.1:
|
||||||
|
version "5.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||||
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2:
|
acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2:
|
||||||
version "1.8.2"
|
version "1.8.2"
|
||||||
resolved "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz"
|
resolved "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz"
|
||||||
@ -842,11 +866,44 @@ acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2:
|
|||||||
acorn-walk "^7.0.0"
|
acorn-walk "^7.0.0"
|
||||||
xtend "^4.0.2"
|
xtend "^4.0.2"
|
||||||
|
|
||||||
|
acorn-private-class-elements@^0.2.7:
|
||||||
|
version "0.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-private-class-elements/-/acorn-private-class-elements-0.2.7.tgz#b14902c705bcff267adede1c9f61c1a317ef95d2"
|
||||||
|
integrity sha512-+GZH2wOKNZOBI4OOPmzpo4cs6mW297sn6fgIk1dUI08jGjhAaEwvC39mN2gJAg2lmAQJ1rBkFqKWonL3Zz6PVA==
|
||||||
|
|
||||||
|
acorn-private-methods@^0.3.3:
|
||||||
|
version "0.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-private-methods/-/acorn-private-methods-0.3.3.tgz#724414ce5b2fec733089d73a5cbba8f7beff75b1"
|
||||||
|
integrity sha512-46oeEol3YFvLSah5m9hGMlNpxDBCEkdceJgf01AjqKYTK9r6HexKs2rgSbLK81pYjZZMonhftuUReGMlbbv05w==
|
||||||
|
dependencies:
|
||||||
|
acorn-private-class-elements "^0.2.7"
|
||||||
|
|
||||||
|
acorn-stage3@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-stage3/-/acorn-stage3-4.0.0.tgz#e8b98ae2a9991be0ba1745b5b626211086b435a8"
|
||||||
|
integrity sha512-BR+LaADtA6GTB5prkNqWmlmCLYmkyW0whvSxdHhbupTaro2qBJ95fJDEiRLPUmiACGHPaYyeH9xmNJWdGfXRQw==
|
||||||
|
dependencies:
|
||||||
|
acorn-class-fields "^0.3.7"
|
||||||
|
acorn-private-methods "^0.3.3"
|
||||||
|
acorn-static-class-features "^0.2.4"
|
||||||
|
|
||||||
|
acorn-static-class-features@^0.2.4:
|
||||||
|
version "0.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-static-class-features/-/acorn-static-class-features-0.2.4.tgz#a0f5261dd483f25196716854f2d7652a1deb39ee"
|
||||||
|
integrity sha512-5X4mpYq5J3pdndLmIB0+WtFd/mKWnNYpuTlTzj32wUu/PMmEGOiayQ5UrqgwdBNiaZBtDDh5kddpP7Yg2QaQYA==
|
||||||
|
dependencies:
|
||||||
|
acorn-private-class-elements "^0.2.7"
|
||||||
|
|
||||||
acorn-walk@^7.0.0, acorn-walk@^7.1.1:
|
acorn-walk@^7.0.0, acorn-walk@^7.1.1:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz"
|
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz"
|
||||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||||
|
|
||||||
|
acorn-walk@^8.0.0:
|
||||||
|
version "8.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
|
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||||
|
|
||||||
acorn@^5.2.1:
|
acorn@^5.2.1:
|
||||||
version "5.7.4"
|
version "5.7.4"
|
||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz"
|
||||||
@ -857,6 +914,11 @@ acorn@^7.0.0, acorn@^7.1.1:
|
|||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
|
acorn@^8.0.4:
|
||||||
|
version "8.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
|
||||||
|
integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
|
||||||
|
|
||||||
acorn@^8.0.5:
|
acorn@^8.0.5:
|
||||||
version "8.8.0"
|
version "8.8.0"
|
||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz"
|
||||||
@ -981,6 +1043,13 @@ app-root-path@^3.0.0:
|
|||||||
resolved "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz"
|
resolved "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz"
|
||||||
integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==
|
integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==
|
||||||
|
|
||||||
|
append-buffer@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1"
|
||||||
|
integrity sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==
|
||||||
|
dependencies:
|
||||||
|
buffer-equal "^1.0.0"
|
||||||
|
|
||||||
aproba@^1.0.3:
|
aproba@^1.0.3:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz"
|
||||||
@ -1521,6 +1590,11 @@ buffer-equal-constant-time@1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
|
||||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||||
|
|
||||||
|
buffer-equal@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.1.tgz#2f7651be5b1b3f057fcd6e7ee16cf34767077d90"
|
||||||
|
integrity sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer-from@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz"
|
||||||
@ -1829,7 +1903,12 @@ cliui@^7.0.2:
|
|||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
wrap-ansi "^7.0.0"
|
wrap-ansi "^7.0.0"
|
||||||
|
|
||||||
clone-deep@^4.0.1:
|
clone-buffer@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
|
||||||
|
integrity sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==
|
||||||
|
|
||||||
|
clone-deep@^4.0.0, clone-deep@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz"
|
||||||
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
|
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
|
||||||
@ -1845,6 +1924,25 @@ clone-response@^1.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-response "^1.0.0"
|
mimic-response "^1.0.0"
|
||||||
|
|
||||||
|
clone-stats@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680"
|
||||||
|
integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==
|
||||||
|
|
||||||
|
clone@^2.1.1:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
|
||||||
|
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
|
||||||
|
|
||||||
|
cloneable-readable@^1.0.0:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec"
|
||||||
|
integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.1"
|
||||||
|
process-nextick-args "^2.0.0"
|
||||||
|
readable-stream "^2.3.5"
|
||||||
|
|
||||||
code-point-at@^1.0.0:
|
code-point-at@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz"
|
||||||
@ -1935,6 +2033,11 @@ commander@^7.0.0:
|
|||||||
resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz"
|
resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz"
|
||||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||||
|
|
||||||
|
commander@^9.0.0:
|
||||||
|
version "9.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd"
|
||||||
|
integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==
|
||||||
|
|
||||||
components-jqueryui@1.12.1:
|
components-jqueryui@1.12.1:
|
||||||
version "1.12.1"
|
version "1.12.1"
|
||||||
resolved "https://registry.npmjs.org/components-jqueryui/-/components-jqueryui-1.12.1.tgz"
|
resolved "https://registry.npmjs.org/components-jqueryui/-/components-jqueryui-1.12.1.tgz"
|
||||||
@ -2027,6 +2130,11 @@ content-type@~1.0.4:
|
|||||||
resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz"
|
resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz"
|
||||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||||
|
|
||||||
|
convert-source-map@^1.5.0:
|
||||||
|
version "1.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
||||||
|
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
|
||||||
|
|
||||||
convert-source-map@~1.1.0:
|
convert-source-map@~1.1.0:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz"
|
||||||
@ -2309,6 +2417,11 @@ deep-is@~0.1.3:
|
|||||||
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
|
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
|
||||||
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
|
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
|
||||||
|
|
||||||
|
deepmerge@^4.0.0:
|
||||||
|
version "4.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||||
|
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||||
|
|
||||||
defer-to-connect@^1.0.1:
|
defer-to-connect@^1.0.1:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz"
|
||||||
@ -2321,6 +2434,14 @@ define-properties@^1.1.2, define-properties@^1.1.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
object-keys "^1.0.12"
|
object-keys "^1.0.12"
|
||||||
|
|
||||||
|
define-properties@^1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
|
||||||
|
integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
|
||||||
|
dependencies:
|
||||||
|
has-property-descriptors "^1.0.0"
|
||||||
|
object-keys "^1.1.1"
|
||||||
|
|
||||||
defined@^1.0.0:
|
defined@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz"
|
||||||
@ -2484,6 +2605,16 @@ duplexer@~0.1.1:
|
|||||||
resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz"
|
resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz"
|
||||||
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
|
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
|
||||||
|
|
||||||
|
duplexify@^3.6.0:
|
||||||
|
version "3.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
||||||
|
integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
|
||||||
|
dependencies:
|
||||||
|
end-of-stream "^1.0.0"
|
||||||
|
inherits "^2.0.1"
|
||||||
|
readable-stream "^2.0.0"
|
||||||
|
stream-shift "^1.0.0"
|
||||||
|
|
||||||
ecc-jsbn@~0.1.1:
|
ecc-jsbn@~0.1.1:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz"
|
resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz"
|
||||||
@ -2561,7 +2692,7 @@ encodeurl@^1.0.2, encodeurl@~1.0.2:
|
|||||||
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
|
||||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||||
|
|
||||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||||
version "1.4.4"
|
version "1.4.4"
|
||||||
resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz"
|
resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz"
|
||||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||||
@ -2576,6 +2707,11 @@ enhanced-resolve@^5.9.3:
|
|||||||
graceful-fs "^4.2.4"
|
graceful-fs "^4.2.4"
|
||||||
tapable "^2.2.0"
|
tapable "^2.2.0"
|
||||||
|
|
||||||
|
ensure-type@^1.5.0:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ensure-type/-/ensure-type-1.5.1.tgz#e65ec8d4c6ee6f35b43675d4243258cb4c8f6159"
|
||||||
|
integrity sha512-Dxe+mVF4MupV6eueWiFa6hUd9OL9lIM2/LqR40k1P+dwG+G2il2UigXTU9aQlaw+Y/N0BKSaTofNw73htTbC5g==
|
||||||
|
|
||||||
entities@^4.3.0:
|
entities@^4.3.0:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz"
|
resolved "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz"
|
||||||
@ -2591,6 +2727,11 @@ envinfo@^7.7.3:
|
|||||||
resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz"
|
resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz"
|
||||||
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
|
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
|
||||||
|
|
||||||
|
eol@^0.9.1:
|
||||||
|
version "0.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd"
|
||||||
|
integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==
|
||||||
|
|
||||||
error-ex@^1.2.0:
|
error-ex@^1.2.0:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz"
|
resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz"
|
||||||
@ -2822,6 +2963,11 @@ eslint-scope@5.1.1:
|
|||||||
esrecurse "^4.3.0"
|
esrecurse "^4.3.0"
|
||||||
estraverse "^4.1.1"
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
|
esprima-next@^5.7.0:
|
||||||
|
version "5.8.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/esprima-next/-/esprima-next-5.8.4.tgz#9f82c8093a33da7207a4e8621e997c66878c145a"
|
||||||
|
integrity sha512-8nYVZ4ioIH4Msjb/XmhnBdz5WRRBaYqevKa1cv9nGJdCehMbzZCPNEEnqfLCZVetUVrUPEcb5IYyu1GG4hFqgg==
|
||||||
|
|
||||||
esprima@^4.0.0, esprima@^4.0.1:
|
esprima@^4.0.0, esprima@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
|
||||||
@ -2938,7 +3084,7 @@ express@4.16.4:
|
|||||||
utils-merge "1.0.1"
|
utils-merge "1.0.1"
|
||||||
vary "~1.1.2"
|
vary "~1.1.2"
|
||||||
|
|
||||||
extend@^3.0.2, extend@~3.0.2:
|
extend@^3.0.0, extend@^3.0.2, extend@~3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
|
resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
|
||||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||||
@ -3076,6 +3222,14 @@ flat@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-buffer "~2.0.3"
|
is-buffer "~2.0.3"
|
||||||
|
|
||||||
|
flush-write-stream@^1.0.2:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
|
||||||
|
integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.3"
|
||||||
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0:
|
||||||
version "1.15.2"
|
version "1.15.2"
|
||||||
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
|
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
|
||||||
@ -3172,6 +3326,14 @@ fs-minipass@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
|
|
||||||
|
fs-mkdirp-stream@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb"
|
||||||
|
integrity sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.11"
|
||||||
|
through2 "^2.0.3"
|
||||||
|
|
||||||
fs.realpath@^1.0.0:
|
fs.realpath@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||||
@ -3295,6 +3457,14 @@ getpass@^0.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
assert-plus "^1.0.0"
|
assert-plus "^1.0.0"
|
||||||
|
|
||||||
|
glob-parent@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
|
||||||
|
integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==
|
||||||
|
dependencies:
|
||||||
|
is-glob "^3.1.0"
|
||||||
|
path-dirname "^1.0.0"
|
||||||
|
|
||||||
glob-parent@~5.1.0:
|
glob-parent@~5.1.0:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
||||||
@ -3302,6 +3472,22 @@ glob-parent@~5.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
|
glob-stream@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4"
|
||||||
|
integrity sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==
|
||||||
|
dependencies:
|
||||||
|
extend "^3.0.0"
|
||||||
|
glob "^7.1.1"
|
||||||
|
glob-parent "^3.1.0"
|
||||||
|
is-negated-glob "^1.0.0"
|
||||||
|
ordered-read-streams "^1.0.0"
|
||||||
|
pumpify "^1.3.5"
|
||||||
|
readable-stream "^2.1.5"
|
||||||
|
remove-trailing-separator "^1.0.1"
|
||||||
|
to-absolute-glob "^2.0.0"
|
||||||
|
unique-stream "^2.0.2"
|
||||||
|
|
||||||
glob-to-regexp@^0.4.1:
|
glob-to-regexp@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
||||||
@ -3343,7 +3529,7 @@ glob@7.1.3:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@^7.1.6, glob@^7.2.0:
|
glob@^7.1.1, glob@^7.1.6, glob@^7.2.0:
|
||||||
version "7.2.3"
|
version "7.2.3"
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
|
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
|
||||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||||
@ -3500,6 +3686,11 @@ got@^9.6.0:
|
|||||||
to-readable-stream "^1.0.0"
|
to-readable-stream "^1.0.0"
|
||||||
url-parse-lax "^3.0.0"
|
url-parse-lax "^3.0.0"
|
||||||
|
|
||||||
|
graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
|
||||||
|
version "4.2.10"
|
||||||
|
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
|
||||||
|
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||||
|
|
||||||
graceful-fs@^4.1.2:
|
graceful-fs@^4.1.2:
|
||||||
version "4.2.4"
|
version "4.2.4"
|
||||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz"
|
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz"
|
||||||
@ -3510,11 +3701,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
|
|||||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz"
|
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz"
|
||||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||||
|
|
||||||
graceful-fs@^4.2.4, graceful-fs@^4.2.9:
|
|
||||||
version "4.2.10"
|
|
||||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
|
|
||||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
|
||||||
|
|
||||||
grain-rpc@0.1.7:
|
grain-rpc@0.1.7:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.npmjs.org/grain-rpc/-/grain-rpc-0.1.7.tgz"
|
resolved "https://registry.npmjs.org/grain-rpc/-/grain-rpc-0.1.7.tgz"
|
||||||
@ -3542,6 +3728,13 @@ gtoken@^5.0.4:
|
|||||||
google-p12-pem "^3.0.3"
|
google-p12-pem "^3.0.3"
|
||||||
jws "^4.0.0"
|
jws "^4.0.0"
|
||||||
|
|
||||||
|
gulp-sort@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/gulp-sort/-/gulp-sort-2.0.0.tgz#c6762a2f1f0de0a3fc595a21599d3fac8dba1aca"
|
||||||
|
integrity sha512-MyTel3FXOdh1qhw1yKhpimQrAmur9q1X0ZigLmCOxouQD+BD3za9/89O+HfbgBQvvh4igEbp0/PUWO+VqGYG1g==
|
||||||
|
dependencies:
|
||||||
|
through2 "^2.0.1"
|
||||||
|
|
||||||
handlebars@4.7.7:
|
handlebars@4.7.7:
|
||||||
version "4.7.7"
|
version "4.7.7"
|
||||||
resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz"
|
resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz"
|
||||||
@ -3582,6 +3775,13 @@ has-flag@^4.0.0:
|
|||||||
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
|
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
|
||||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||||
|
|
||||||
|
has-property-descriptors@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
|
||||||
|
integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
|
||||||
|
dependencies:
|
||||||
|
get-intrinsic "^1.1.1"
|
||||||
|
|
||||||
has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2:
|
has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz"
|
||||||
@ -3760,6 +3960,39 @@ i18next-http-middleware@3.2.1:
|
|||||||
resolved "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.2.1.tgz"
|
resolved "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.2.1.tgz"
|
||||||
integrity sha512-zBwXxDChT0YLoTXIR6jRuqnUUhXW0Iw7egoTnNXyaDRtTbfWNXwU0a53ThyuRPQ+k+tXu3ZMNKRzfLuononaRw==
|
integrity sha512-zBwXxDChT0YLoTXIR6jRuqnUUhXW0Iw7egoTnNXyaDRtTbfWNXwU0a53ThyuRPQ+k+tXu3ZMNKRzfLuononaRw==
|
||||||
|
|
||||||
|
i18next-scanner@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/i18next-scanner/-/i18next-scanner-4.1.0.tgz#15979dcfd6eca494e08c8a8ff784e8740ab7d318"
|
||||||
|
integrity sha512-KAZRc6ZBqtn/Qa5fRRTn7D8effB84K9m3g9SQcKD+tocMajEVW2zfFP397qNxROad1VFjUGT8aw1Y6ZcfnIzlg==
|
||||||
|
dependencies:
|
||||||
|
acorn "^8.0.4"
|
||||||
|
acorn-dynamic-import "^4.0.0"
|
||||||
|
acorn-jsx "^5.3.1"
|
||||||
|
acorn-stage3 "^4.0.0"
|
||||||
|
acorn-walk "^8.0.0"
|
||||||
|
chalk "^4.1.0"
|
||||||
|
clone-deep "^4.0.0"
|
||||||
|
commander "^9.0.0"
|
||||||
|
deepmerge "^4.0.0"
|
||||||
|
ensure-type "^1.5.0"
|
||||||
|
eol "^0.9.1"
|
||||||
|
esprima-next "^5.7.0"
|
||||||
|
gulp-sort "^2.0.0"
|
||||||
|
i18next "*"
|
||||||
|
lodash "^4.0.0"
|
||||||
|
parse5 "^6.0.0"
|
||||||
|
sortobject "^4.0.0"
|
||||||
|
through2 "^4.0.0"
|
||||||
|
vinyl "^2.2.0"
|
||||||
|
vinyl-fs "^3.0.1"
|
||||||
|
|
||||||
|
i18next@*:
|
||||||
|
version "22.4.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.5.tgz#7324e4946c2facbe743ca25bca8980af05b0a109"
|
||||||
|
integrity sha512-Kc+Ow0guRetUq+kv02tj0Yof9zveROPBAmJ8UxxNODLVBRSwsM4iD0Gw3BEieOmkWemF6clU3K1fbnCuTqiN2Q==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.20.6"
|
||||||
|
|
||||||
i18next@21.9.1, i18next@^21.0.1:
|
i18next@21.9.1, i18next@^21.0.1:
|
||||||
version "21.9.1"
|
version "21.9.1"
|
||||||
resolved "https://registry.npmjs.org/i18next/-/i18next-21.9.1.tgz"
|
resolved "https://registry.npmjs.org/i18next/-/i18next-21.9.1.tgz"
|
||||||
@ -3907,6 +4140,14 @@ ipaddr.js@^2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
|
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
|
||||||
integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
|
integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
|
||||||
|
|
||||||
|
is-absolute@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576"
|
||||||
|
integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==
|
||||||
|
dependencies:
|
||||||
|
is-relative "^1.0.0"
|
||||||
|
is-windows "^1.0.1"
|
||||||
|
|
||||||
is-arguments@^1.0.4:
|
is-arguments@^1.0.4:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
||||||
@ -3939,7 +4180,7 @@ is-boolean-object@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
call-bind "^1.0.0"
|
call-bind "^1.0.0"
|
||||||
|
|
||||||
is-buffer@^1.1.0:
|
is-buffer@^1.1.0, is-buffer@^1.1.5:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz"
|
resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz"
|
||||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
@ -3985,7 +4226,7 @@ is-date-object@^1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz"
|
||||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
||||||
|
|
||||||
is-extglob@^2.1.1:
|
is-extglob@^2.1.0, is-extglob@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
|
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
|
||||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||||
@ -4014,6 +4255,13 @@ is-generator-function@^1.0.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
|
is-glob@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
|
||||||
|
integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==
|
||||||
|
dependencies:
|
||||||
|
is-extglob "^2.1.0"
|
||||||
|
|
||||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz"
|
||||||
@ -4029,6 +4277,11 @@ is-installed-globally@^0.3.1:
|
|||||||
global-dirs "^2.0.1"
|
global-dirs "^2.0.1"
|
||||||
is-path-inside "^3.0.1"
|
is-path-inside "^3.0.1"
|
||||||
|
|
||||||
|
is-negated-glob@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
|
||||||
|
integrity sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==
|
||||||
|
|
||||||
is-negative-zero@^2.0.1:
|
is-negative-zero@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz"
|
||||||
@ -4108,6 +4361,13 @@ is-regex@^1.1.2:
|
|||||||
call-bind "^1.0.2"
|
call-bind "^1.0.2"
|
||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
|
is-relative@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d"
|
||||||
|
integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==
|
||||||
|
dependencies:
|
||||||
|
is-unc-path "^1.0.0"
|
||||||
|
|
||||||
is-retry-allowed@^1.0.0:
|
is-retry-allowed@^1.0.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz"
|
||||||
@ -4151,11 +4411,33 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
|
||||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||||
|
|
||||||
|
is-unc-path@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d"
|
||||||
|
integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==
|
||||||
|
dependencies:
|
||||||
|
unc-path-regex "^0.1.2"
|
||||||
|
|
||||||
is-url@^1.2.4:
|
is-url@^1.2.4:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz"
|
resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz"
|
||||||
integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
|
integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
|
||||||
|
|
||||||
|
is-utf8@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
||||||
|
integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==
|
||||||
|
|
||||||
|
is-valid-glob@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa"
|
||||||
|
integrity sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==
|
||||||
|
|
||||||
|
is-windows@^1.0.1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||||
|
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
|
||||||
|
|
||||||
is-yarn-global@^0.3.0:
|
is-yarn-global@^0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz"
|
||||||
@ -4306,6 +4588,11 @@ json-schema@0.2.3:
|
|||||||
resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz"
|
resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz"
|
||||||
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
|
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
|
||||||
|
|
||||||
|
json-stable-stringify-without-jsonify@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||||
|
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||||
|
|
||||||
json-stable-stringify@~0.0.0:
|
json-stable-stringify@~0.0.0:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz"
|
resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz"
|
||||||
@ -4465,6 +4752,13 @@ lazystream@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^2.0.5"
|
readable-stream "^2.0.5"
|
||||||
|
|
||||||
|
lead@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42"
|
||||||
|
integrity sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==
|
||||||
|
dependencies:
|
||||||
|
flush-write-stream "^1.0.2"
|
||||||
|
|
||||||
levn@~0.3.0:
|
levn@~0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz"
|
||||||
@ -4633,7 +4927,7 @@ lodash.uniq@^4.5.0:
|
|||||||
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
|
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
|
||||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||||
|
|
||||||
lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0:
|
lodash@4.17.21, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
|
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
@ -5189,6 +5483,13 @@ nopt@~1.0.10:
|
|||||||
dependencies:
|
dependencies:
|
||||||
abbrev "1"
|
abbrev "1"
|
||||||
|
|
||||||
|
normalize-path@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
|
||||||
|
integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==
|
||||||
|
dependencies:
|
||||||
|
remove-trailing-separator "^1.0.1"
|
||||||
|
|
||||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
|
||||||
@ -5199,6 +5500,13 @@ normalize-url@^4.1.0:
|
|||||||
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz"
|
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz"
|
||||||
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
|
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
|
||||||
|
|
||||||
|
now-and-later@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c"
|
||||||
|
integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==
|
||||||
|
dependencies:
|
||||||
|
once "^1.3.2"
|
||||||
|
|
||||||
npm-bundled@^1.0.1:
|
npm-bundled@^1.0.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz"
|
||||||
@ -5285,6 +5593,16 @@ object.assign@4.1.0:
|
|||||||
has-symbols "^1.0.0"
|
has-symbols "^1.0.0"
|
||||||
object-keys "^1.0.11"
|
object-keys "^1.0.11"
|
||||||
|
|
||||||
|
object.assign@^4.0.4:
|
||||||
|
version "4.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
|
||||||
|
integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
define-properties "^1.1.4"
|
||||||
|
has-symbols "^1.0.3"
|
||||||
|
object-keys "^1.1.1"
|
||||||
|
|
||||||
object.assign@^4.1.2:
|
object.assign@^4.1.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz"
|
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz"
|
||||||
@ -5316,7 +5634,7 @@ on-headers@~1.0.1, on-headers@~1.0.2:
|
|||||||
resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz"
|
||||||
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
|
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
|
||||||
|
|
||||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
|
||||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||||
@ -5335,6 +5653,13 @@ optionator@^0.8.1:
|
|||||||
type-check "~0.3.2"
|
type-check "~0.3.2"
|
||||||
word-wrap "~1.2.3"
|
word-wrap "~1.2.3"
|
||||||
|
|
||||||
|
ordered-read-streams@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e"
|
||||||
|
integrity sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==
|
||||||
|
dependencies:
|
||||||
|
readable-stream "^2.0.1"
|
||||||
|
|
||||||
os-browserify@~0.3.0:
|
os-browserify@~0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz"
|
||||||
@ -5463,7 +5788,7 @@ parse5@*:
|
|||||||
dependencies:
|
dependencies:
|
||||||
entities "^4.3.0"
|
entities "^4.3.0"
|
||||||
|
|
||||||
parse5@6.0.1, parse5@^6.0.1:
|
parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz"
|
resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz"
|
||||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||||
@ -5483,6 +5808,11 @@ path-browserify@~0.0.0:
|
|||||||
resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz"
|
resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz"
|
||||||
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
|
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
|
||||||
|
|
||||||
|
path-dirname@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
||||||
|
integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==
|
||||||
|
|
||||||
path-exists@^3.0.0:
|
path-exists@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz"
|
||||||
@ -5722,16 +6052,16 @@ printj@~1.1.0:
|
|||||||
resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz"
|
||||||
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
|
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
|
||||||
|
|
||||||
|
process-nextick-args@^2.0.0, process-nextick-args@~2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
|
||||||
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
process-nextick-args@~1.0.6:
|
process-nextick-args@~1.0.6:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
|
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
|
||||||
integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=
|
integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
|
|
||||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
|
||||||
|
|
||||||
process@~0.11.0:
|
process@~0.11.0:
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
|
resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
|
||||||
@ -5782,6 +6112,14 @@ public-encrypt@^4.0.0:
|
|||||||
randombytes "^2.0.1"
|
randombytes "^2.0.1"
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
|
|
||||||
|
pump@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
|
||||||
|
integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
|
||||||
|
dependencies:
|
||||||
|
end-of-stream "^1.1.0"
|
||||||
|
once "^1.3.1"
|
||||||
|
|
||||||
pump@^3.0.0:
|
pump@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
|
||||||
@ -5790,6 +6128,15 @@ pump@^3.0.0:
|
|||||||
end-of-stream "^1.1.0"
|
end-of-stream "^1.1.0"
|
||||||
once "^1.3.1"
|
once "^1.3.1"
|
||||||
|
|
||||||
|
pumpify@^1.3.5:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
|
||||||
|
integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
|
||||||
|
dependencies:
|
||||||
|
duplexify "^3.6.0"
|
||||||
|
inherits "^2.0.3"
|
||||||
|
pump "^2.0.0"
|
||||||
|
|
||||||
punycode@1.3.2:
|
punycode@1.3.2:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz"
|
resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz"
|
||||||
@ -5929,7 +6276,7 @@ read-only-stream@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^2.0.2"
|
readable-stream "^2.0.2"
|
||||||
|
|
||||||
"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz"
|
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz"
|
||||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||||
@ -5938,7 +6285,7 @@ read-only-stream@^2.0.0:
|
|||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
||||||
version "2.3.7"
|
version "2.3.7"
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
|
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
|
||||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||||
@ -6051,6 +6398,11 @@ reflect-metadata@^0.1.13:
|
|||||||
resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz"
|
resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz"
|
||||||
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.11:
|
||||||
|
version "0.13.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||||
|
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||||
|
|
||||||
regenerator-runtime@^0.13.4:
|
regenerator-runtime@^0.13.4:
|
||||||
version "0.13.9"
|
version "0.13.9"
|
||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
||||||
@ -6070,6 +6422,33 @@ registry-url@^5.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
rc "^1.2.8"
|
rc "^1.2.8"
|
||||||
|
|
||||||
|
remove-bom-buffer@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53"
|
||||||
|
integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==
|
||||||
|
dependencies:
|
||||||
|
is-buffer "^1.1.5"
|
||||||
|
is-utf8 "^0.2.1"
|
||||||
|
|
||||||
|
remove-bom-stream@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523"
|
||||||
|
integrity sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==
|
||||||
|
dependencies:
|
||||||
|
remove-bom-buffer "^3.0.0"
|
||||||
|
safe-buffer "^5.1.0"
|
||||||
|
through2 "^2.0.3"
|
||||||
|
|
||||||
|
remove-trailing-separator@^1.0.1:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||||
|
integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==
|
||||||
|
|
||||||
|
replace-ext@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a"
|
||||||
|
integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==
|
||||||
|
|
||||||
request-promise-core@1.1.4:
|
request-promise-core@1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz"
|
resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz"
|
||||||
@ -6139,6 +6518,13 @@ resolve-from@^5.0.0:
|
|||||||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
|
||||||
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||||
|
|
||||||
|
resolve-options@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131"
|
||||||
|
integrity sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==
|
||||||
|
dependencies:
|
||||||
|
value-or-function "^3.0.0"
|
||||||
|
|
||||||
resolve@1.1.7:
|
resolve@1.1.7:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz"
|
resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz"
|
||||||
@ -6483,6 +6869,11 @@ sinon@7.1.1:
|
|||||||
supports-color "^5.5.0"
|
supports-color "^5.5.0"
|
||||||
type-detect "^4.0.8"
|
type-detect "^4.0.8"
|
||||||
|
|
||||||
|
sortobject@^4.0.0:
|
||||||
|
version "4.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sortobject/-/sortobject-4.16.0.tgz#9f1deca8da47f0d2da0a9100d9b0a407fd9d7b3f"
|
||||||
|
integrity sha512-jdcWhqJjxyYxRcXa30qImF3PZea1GpNwdKxUac28T28+GodptH4XihPuRlgCY0hITIEQVnw8DtQ81Fb6fomBaw==
|
||||||
|
|
||||||
source-list-map@^2.0.1:
|
source-list-map@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz"
|
||||||
@ -6608,6 +6999,11 @@ stream-http@^2.0.0:
|
|||||||
to-arraybuffer "^1.0.0"
|
to-arraybuffer "^1.0.0"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
|
stream-shift@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
||||||
|
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
|
||||||
|
|
||||||
stream-splicer@^2.0.0:
|
stream-splicer@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz"
|
||||||
@ -6934,7 +7330,15 @@ thirty-two@^1.0.2:
|
|||||||
resolved "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz"
|
||||||
integrity sha1-TKL//AKlEpDSdEueP1V2k8prYno=
|
integrity sha1-TKL//AKlEpDSdEueP1V2k8prYno=
|
||||||
|
|
||||||
through2@^2.0.0:
|
through2-filter@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254"
|
||||||
|
integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==
|
||||||
|
dependencies:
|
||||||
|
through2 "~2.0.0"
|
||||||
|
xtend "~4.0.0"
|
||||||
|
|
||||||
|
through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
|
resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
|
||||||
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
||||||
@ -6950,6 +7354,13 @@ through2@^3.0.1:
|
|||||||
inherits "^2.0.4"
|
inherits "^2.0.4"
|
||||||
readable-stream "2 || 3"
|
readable-stream "2 || 3"
|
||||||
|
|
||||||
|
through2@^4.0.0:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764"
|
||||||
|
integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==
|
||||||
|
dependencies:
|
||||||
|
readable-stream "3"
|
||||||
|
|
||||||
"through@>=2.2.7 <3", through@~2.3.4:
|
"through@>=2.2.7 <3", through@~2.3.4:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
|
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
|
||||||
@ -6996,6 +7407,14 @@ tmp@^0.2.0, tmp@^0.2.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
rimraf "^3.0.0"
|
rimraf "^3.0.0"
|
||||||
|
|
||||||
|
to-absolute-glob@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b"
|
||||||
|
integrity sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==
|
||||||
|
dependencies:
|
||||||
|
is-absolute "^1.0.0"
|
||||||
|
is-negated-glob "^1.0.0"
|
||||||
|
|
||||||
to-arraybuffer@^1.0.0:
|
to-arraybuffer@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz"
|
||||||
@ -7013,6 +7432,13 @@ to-regex-range@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number "^7.0.0"
|
is-number "^7.0.0"
|
||||||
|
|
||||||
|
to-through@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6"
|
||||||
|
integrity sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==
|
||||||
|
dependencies:
|
||||||
|
through2 "^2.0.3"
|
||||||
|
|
||||||
toidentifier@1.0.0:
|
toidentifier@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz"
|
||||||
@ -7222,6 +7648,11 @@ unbox-primitive@^1.0.0:
|
|||||||
has-symbols "^1.0.2"
|
has-symbols "^1.0.2"
|
||||||
which-boxed-primitive "^1.0.2"
|
which-boxed-primitive "^1.0.2"
|
||||||
|
|
||||||
|
unc-path-regex@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
||||||
|
integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==
|
||||||
|
|
||||||
undeclared-identifiers@^1.1.2:
|
undeclared-identifiers@^1.1.2:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz"
|
||||||
@ -7245,6 +7676,14 @@ underscore@1.12.1, underscore@>=1.8.3, underscore@^1.8.0:
|
|||||||
resolved "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz"
|
resolved "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz"
|
||||||
integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==
|
integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==
|
||||||
|
|
||||||
|
unique-stream@^2.0.2:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac"
|
||||||
|
integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==
|
||||||
|
dependencies:
|
||||||
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
|
through2-filter "^3.0.0"
|
||||||
|
|
||||||
unique-string@^2.0.0:
|
unique-string@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz"
|
||||||
@ -7407,6 +7846,11 @@ uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2:
|
|||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
||||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
|
value-or-function@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813"
|
||||||
|
integrity sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==
|
||||||
|
|
||||||
vary@~1.1.2:
|
vary@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
|
||||||
@ -7421,6 +7865,54 @@ verror@1.10.0:
|
|||||||
core-util-is "1.0.2"
|
core-util-is "1.0.2"
|
||||||
extsprintf "^1.2.0"
|
extsprintf "^1.2.0"
|
||||||
|
|
||||||
|
vinyl-fs@^3.0.1:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7"
|
||||||
|
integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==
|
||||||
|
dependencies:
|
||||||
|
fs-mkdirp-stream "^1.0.0"
|
||||||
|
glob-stream "^6.1.0"
|
||||||
|
graceful-fs "^4.0.0"
|
||||||
|
is-valid-glob "^1.0.0"
|
||||||
|
lazystream "^1.0.0"
|
||||||
|
lead "^1.0.0"
|
||||||
|
object.assign "^4.0.4"
|
||||||
|
pumpify "^1.3.5"
|
||||||
|
readable-stream "^2.3.3"
|
||||||
|
remove-bom-buffer "^3.0.0"
|
||||||
|
remove-bom-stream "^1.2.0"
|
||||||
|
resolve-options "^1.1.0"
|
||||||
|
through2 "^2.0.0"
|
||||||
|
to-through "^2.0.0"
|
||||||
|
value-or-function "^3.0.0"
|
||||||
|
vinyl "^2.0.0"
|
||||||
|
vinyl-sourcemap "^1.1.0"
|
||||||
|
|
||||||
|
vinyl-sourcemap@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16"
|
||||||
|
integrity sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==
|
||||||
|
dependencies:
|
||||||
|
append-buffer "^1.0.2"
|
||||||
|
convert-source-map "^1.5.0"
|
||||||
|
graceful-fs "^4.1.6"
|
||||||
|
normalize-path "^2.1.1"
|
||||||
|
now-and-later "^2.0.0"
|
||||||
|
remove-bom-buffer "^3.0.0"
|
||||||
|
vinyl "^2.0.0"
|
||||||
|
|
||||||
|
vinyl@^2.0.0, vinyl@^2.2.0:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974"
|
||||||
|
integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==
|
||||||
|
dependencies:
|
||||||
|
clone "^2.1.1"
|
||||||
|
clone-buffer "^1.0.0"
|
||||||
|
clone-stats "^1.0.0"
|
||||||
|
cloneable-readable "^1.0.0"
|
||||||
|
remove-trailing-separator "^1.0.1"
|
||||||
|
replace-ext "^1.0.0"
|
||||||
|
|
||||||
vm-browserify@~0.0.1:
|
vm-browserify@~0.0.1:
|
||||||
version "0.0.4"
|
version "0.0.4"
|
||||||
resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz"
|
resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz"
|
||||||
@ -7800,7 +8292,7 @@ xpath@0.0.27:
|
|||||||
resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz"
|
resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz"
|
||||||
integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==
|
integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"
|
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"
|
||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
Loading…
Reference in New Issue
Block a user