2020-10-02 15:10:00 +00:00
|
|
|
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, 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),
|
2020-12-14 17:42:09 +00:00
|
|
|
cancelRecoveryMode: getCancelRecoveryModeFn(gristDoc),
|
2021-01-15 21:56:58 +00:00
|
|
|
cancelUserOverride: getCancelUserOverrideFn(gristDoc),
|
2020-10-02 15:10:00 +00:00
|
|
|
isPageNameReadOnly: (use) => use(gristDoc.isReadonly) || typeof use(gristDoc.activeViewId) !== 'number',
|
|
|
|
isDocNameReadOnly: (use) => use(gristDoc.isReadonly) || use(pageModel.isFork),
|
|
|
|
isFork: pageModel.isFork,
|
2020-12-14 17:42:09 +00:00
|
|
|
isRecoveryMode: pageModel.isRecoveryMode,
|
2021-01-15 21:56:58 +00:00
|
|
|
userOverride: pageModel.userOverride,
|
2020-10-02 15:10:00 +00:00
|
|
|
isFiddle: Computed.create(owner, (use) => use(pageModel.isPrefork) && !use(pageModel.isSample)),
|
|
|
|
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),
|
|
|
|
|
|
|
|
buildNotifyMenuButton(appModel.notifier, appModel),
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-14 17:42:09 +00:00
|
|
|
function getCancelRecoveryModeFn(gristDoc: GristDoc): () => Promise<void> {
|
|
|
|
return async () => {
|
|
|
|
await gristDoc.app.topAppModel.api.getDocAPI(gristDoc.docPageModel.currentDocId.get()!)
|
|
|
|
.recover(false);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-01-15 21:56:58 +00:00
|
|
|
function getCancelUserOverrideFn(gristDoc: GristDoc): () => Promise<void> {
|
|
|
|
return async () => {
|
|
|
|
const url = new URL(window.location.href);
|
|
|
|
url.searchParams.delete('aclAsUser_');
|
|
|
|
url.searchParams.delete('aclAsUserId_');
|
|
|
|
window.location.assign(url.href);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-10-02 15:10:00 +00:00
|
|
|
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: 0px;
|
|
|
|
overflow: hidden;
|
|
|
|
`);
|
|
|
|
|
|
|
|
const cssFlexSpace = styled('div', `
|
|
|
|
flex: 1 1 0px;
|
|
|
|
`);
|
|
|
|
|
|
|
|
const cssSpacer = styled('div', `
|
|
|
|
width: 10px;
|
|
|
|
`);
|