mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
24fc3a2d00
Summary: The old Examples and Templates workspace is now a page that pulls templates from a new public Grist Templates org. The All Documents view will pull featured templates from that org, where featured templates are simply pinned documents in Grist Templates. The Examples and Templates page will also show the featured templates, as well as the rest of the available templates organized by category. The categories are equivalent to workspaces in Grist Templates, and are generated dynamically. Test Plan: Browser tests. Reviewers: paulfitz, dsagal Reviewed By: paulfitz, dsagal Subscribers: dsagal, paulfitz, jarek Differential Revision: https://phab.getgrist.com/D2930
142 lines
5.2 KiB
TypeScript
142 lines
5.2 KiB
TypeScript
import {GristDoc} from 'app/client/components/GristDoc';
|
|
import {loadSearch} from 'app/client/lib/imports';
|
|
import {AppModel, reportError} from 'app/client/models/AppModel';
|
|
import {DocPageModel} from 'app/client/models/DocPageModel';
|
|
import {workspaceName} from 'app/client/models/WorkspaceInfo';
|
|
import {AccountWidget} from 'app/client/ui/AccountWidget';
|
|
import {buildNotifyMenuButton} from 'app/client/ui/NotifyUI';
|
|
import {buildShareMenuButton} from 'app/client/ui/ShareMenu';
|
|
import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss';
|
|
import {docBreadcrumbs} from 'app/client/ui2018/breadcrumbs';
|
|
import {colors, cssHideForNarrowScreen, testId} from 'app/client/ui2018/cssVars';
|
|
import {IconName} from 'app/client/ui2018/IconList';
|
|
import {waitGrainObs} from 'app/common/gutil';
|
|
import {Computed, dom, DomElementArg, makeTestId, MultiHolder, Observable, styled} from 'grainjs';
|
|
|
|
export function createTopBarHome(appModel: AppModel) {
|
|
return [
|
|
cssFlexSpace(),
|
|
buildNotifyMenuButton(appModel.notifier, appModel),
|
|
dom('div', dom.create(AccountWidget, appModel)),
|
|
];
|
|
}
|
|
|
|
export function createTopBarDoc(owner: MultiHolder, appModel: AppModel, pageModel: DocPageModel, allCommands: any) {
|
|
const doc = pageModel.currentDoc;
|
|
const renameDoc = (val: string) => pageModel.renameDoc(val);
|
|
const displayNameWs = Computed.create(owner, pageModel.currentWorkspace,
|
|
(use, ws) => ws ? {...ws, name: workspaceName(appModel, ws)} : ws);
|
|
const searchBarContent = Observable.create<HTMLElement|null>(owner, null);
|
|
|
|
loadSearch()
|
|
.then(async module => {
|
|
const model = module.SearchModelImpl.create(owner, (await waitGrainObs(pageModel.gristDoc))!);
|
|
searchBarContent.set(module.searchBar(model, makeTestId('test-tb-search-')));
|
|
})
|
|
.catch(reportError);
|
|
|
|
return [
|
|
// TODO Before gristDoc is loaded, we could show doc-name without the page. For now, we delay
|
|
// showing of breadcrumbs until gristDoc is loaded.
|
|
dom.maybe(pageModel.gristDoc, (gristDoc) =>
|
|
cssBreadcrumbContainer(
|
|
docBreadcrumbs(displayNameWs, pageModel.currentDocTitle, gristDoc.currentPageName, {
|
|
docNameSave: renameDoc,
|
|
pageNameSave: getRenamePageFn(gristDoc),
|
|
cancelRecoveryMode: getCancelRecoveryModeFn(gristDoc),
|
|
isPageNameReadOnly: (use) => use(gristDoc.isReadonly) || typeof use(gristDoc.activeViewId) !== 'number',
|
|
isDocNameReadOnly: (use) => use(gristDoc.isReadonly) || use(pageModel.isFork),
|
|
isFork: pageModel.isFork,
|
|
isRecoveryMode: pageModel.isRecoveryMode,
|
|
userOverride: pageModel.userOverride,
|
|
isFiddle: Computed.create(owner, (use) => use(pageModel.isPrefork)),
|
|
isSnapshot: Computed.create(owner, doc, (use, _doc) => Boolean(_doc && _doc.idParts.snapshotId)),
|
|
isPublic: Computed.create(owner, doc, (use, _doc) => Boolean(_doc && _doc.public)),
|
|
})
|
|
)
|
|
),
|
|
cssFlexSpace(),
|
|
|
|
// Don't show useless undo/redo buttons for sample docs, to leave more space for "Make copy".
|
|
dom.maybe(pageModel.undoState, (state) => [
|
|
topBarUndoBtn('Undo',
|
|
dom.on('click', () => state.isUndoDisabled.get() || allCommands.undo.run()),
|
|
cssHoverCircle.cls('-disabled', state.isUndoDisabled),
|
|
testId('undo')
|
|
),
|
|
topBarUndoBtn('Redo',
|
|
dom.on('click', () => state.isRedoDisabled.get() || allCommands.redo.run()),
|
|
cssHoverCircle.cls('-disabled', state.isRedoDisabled),
|
|
testId('redo')
|
|
),
|
|
cssSpacer(),
|
|
]),
|
|
dom.domComputed(searchBarContent),
|
|
|
|
buildShareMenuButton(pageModel),
|
|
|
|
dom.update(
|
|
buildNotifyMenuButton(appModel.notifier, appModel),
|
|
cssHideForNarrowScreen.cls(''),
|
|
),
|
|
|
|
dom('div', dom.create(AccountWidget, appModel, pageModel))
|
|
];
|
|
}
|
|
|
|
// Given the GristDoc instance, returns a rename function for the current active page.
|
|
// If the current page is not able to be renamed or the new name is invalid, the function is a noop.
|
|
function getRenamePageFn(gristDoc: GristDoc): (val: string) => Promise<void> {
|
|
return async (val: string) => {
|
|
const views = gristDoc.docModel.views;
|
|
const viewId = gristDoc.activeViewId.get();
|
|
if (typeof viewId === 'number' && val.length > 0) {
|
|
const name = views.rowModels[viewId].name;
|
|
await name.saveOnly(val);
|
|
}
|
|
};
|
|
}
|
|
|
|
function getCancelRecoveryModeFn(gristDoc: GristDoc): () => Promise<void> {
|
|
return async () => {
|
|
await gristDoc.app.topAppModel.api.getDocAPI(gristDoc.docPageModel.currentDocId.get()!)
|
|
.recover(false);
|
|
};
|
|
}
|
|
|
|
function topBarUndoBtn(iconName: IconName, ...domArgs: DomElementArg[]): Element {
|
|
return cssHoverCircle(
|
|
cssTopBarUndoBtn(iconName),
|
|
...domArgs
|
|
);
|
|
}
|
|
|
|
const cssTopBarUndoBtn = styled(cssTopBarBtn, `
|
|
background-color: ${colors.slate};
|
|
|
|
.${cssHoverCircle.className}:hover & {
|
|
background-color: ${colors.lightGreen};
|
|
}
|
|
|
|
.${cssHoverCircle.className}-disabled:hover & {
|
|
background-color: ${colors.darkGrey};
|
|
cursor: default;
|
|
}
|
|
`);
|
|
|
|
const cssBreadcrumbContainer = styled('div', `
|
|
padding: 7px;
|
|
flex: 1 1 auto;
|
|
min-width: 24px;
|
|
overflow: hidden;
|
|
`);
|
|
|
|
const cssFlexSpace = styled('div', `
|
|
flex: 1 1 0px;
|
|
`);
|
|
|
|
const cssSpacer = styled('div', `
|
|
max-width: 10px;
|
|
flex: auto;
|
|
`);
|