import {makeT} from 'app/client/lib/localization'; import {GristDoc} from 'app/client/components/GristDoc'; import {cssInput} from 'app/client/ui/cssInput'; import {cssField} from 'app/client/ui/MakeCopyMenu'; import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox'; import {colors} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links'; import {saveModal} from 'app/client/ui2018/modals'; import {commonUrls} from 'app/common/gristUrls'; import {Computed, Disposable, dom, input, makeTestId, Observable, styled} from 'grainjs'; const t = makeT('DuplicateTable'); const testId = makeTestId('test-duplicate-table-'); /** * Response returned by a DuplicateTable user action. */ export interface DuplicateTableResponse { /** Row id of the new table. */ id: number; /** Table id of the new table. */ table_id: string; /** Row id of the new raw view section. */ raw_section_id: number; } export interface DuplicateTableOptions { onSuccess?(response: DuplicateTableResponse): void; } /** * Shows a modal with options for duplicating the table `tableId`. */ export function duplicateTable( gristDoc: GristDoc, tableId: string, {onSuccess}: DuplicateTableOptions = {} ) { saveModal((_ctl, owner) => { const duplicateTableModal = DuplicateTableModal.create(owner, gristDoc, tableId); return { title: 'Duplicate Table', body: duplicateTableModal.buildDom(), saveFunc: async () => { const response = await duplicateTableModal.save(); onSuccess?.(response); }, saveDisabled: duplicateTableModal.saveDisabled, width: 'normal', }; }); } class DuplicateTableModal extends Disposable { private _newTableName = Observable.create(this, ''); private _includeData = Observable.create(this, false); private _saveDisabled = Computed.create(this, this._newTableName, (_use, name) => !name.trim()); constructor(private _gristDoc: GristDoc, private _tableId: string) { super(); } public get saveDisabled() { return this._saveDisabled; } public save() { return this._duplicateTable(); } public buildDom() { return [ cssField( input( this._newTableName, {onInput: true}, {placeholder: t("Name for new table")}, (elem) => { setTimeout(() => { elem.focus(); }, 20); }, dom.on('focus', (_ev, elem) => { elem.select(); }), dom.cls(cssInput.className), testId('name'), ), ), cssWarning( cssWarningIcon('Warning'), dom('div', 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.')} )), ), cssField( cssCheckbox( this._includeData, t("Copy all data in addition to the table structure."), testId('copy-all-data'), ), ), dom.maybe(this._includeData, () => cssWarning( cssWarningIcon('Warning'), dom('div', t("Only the document default access rules will apply to the copy.")), testId('acl-warning'), )), ]; } private _duplicateTable() { const {docData} = this._gristDoc; const [newTableName, includeData] = [this._newTableName.get(), this._includeData.get()]; return docData.sendAction(['DuplicateTable', this._tableId, newTableName, includeData]); } } const cssCheckbox = styled(labeledSquareCheckbox, ` margin-top: 8px; `); const cssWarning = styled('div', ` display: flex; column-gap: 8px; `); const cssWarningIcon = styled(icon, ` --icon-color: ${colors.orange}; flex-shrink: 0; `);