gristlabs_grist-core/app/client/widgets/CellStyle.ts
George Gevoian caf830db08 (core) Record Cards
Summary:
Adds a new Record Card view section to each non-summary table, which can be from opened from various parts of the Grist UI to view and edit records in a popup card view.

Work is still ongoing, so the feature is locked away behind a flag; follow-up work is planned to finish up the implementation and add end-to-end tests.

Test Plan: Python and server tests. Browser tests will be included in a follow-up.

Reviewers: jarek, paulfitz

Reviewed By: jarek

Subscribers: paulfitz

Differential Revision: https://phab.getgrist.com/D4114
2023-11-19 20:12:37 -05:00

164 lines
6.0 KiB
TypeScript

import { makeT } from 'app/client/lib/localization';
import {allCommands} from 'app/client/components/commands';
import {GristDoc} from 'app/client/components/GristDoc';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {textButton} from 'app/client/ui2018/buttons';
import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect';
import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {ConditionalStyle} from 'app/client/widgets/ConditionalStyle';
import {Computed, Disposable, dom, DomContents, fromKo, styled} from 'grainjs';
const t = makeT('CellStyle');
export class CellStyle extends Disposable {
constructor(
private _field: ViewFieldRec,
private _gristDoc: GristDoc,
private _defaultTextColor: string|undefined
) {
super();
}
public buildDom(): DomContents {
const isTableWidget = this._field.viewSection().parentKey() === 'record';
return [
dom.maybe(use => isTableWidget, () => {
return [
cssLine(
cssLabel(t('HEADER STYLE')),
),
cssRow(
testId('header-color-select'),
dom.domComputedOwned(fromKo(this._field.config.headerStyle), (holder, options) => {
const headerTextColor = fromKo(options.prop("headerTextColor"));
const headerFillColor = fromKo(options.prop("headerFillColor"));
const headerFontBold = fromKo(options.prop("headerFontBold"));
const headerFontUnderline = fromKo(options.prop("headerFontUnderline"));
const headerFontItalic = fromKo(options.prop("headerFontItalic"));
const headerFontStrikethrough = fromKo(options.prop("headerFontStrikethrough"));
const hasMixedStyle = Computed.create(holder, use => {
if (!use(this._field.config.multiselect)) { return false; }
const commonStyle = [
use(options.mixed('headerTextColor')),
use(options.mixed('headerFillColor')),
use(options.mixed('headerFontBold')),
use(options.mixed('headerFontUnderline')),
use(options.mixed('headerFontItalic')),
use(options.mixed('headerFontStrikethrough'))
];
return commonStyle.some(Boolean);
});
return colorSelect(
{
textColor: new ColorOption({
color: headerTextColor,
defaultColor: theme.tableHeaderFg.toString(),
noneText: 'default',
}),
fillColor: new ColorOption({
color: headerFillColor,
allowsNone: true,
noneText: 'none',
}),
fontBold: headerFontBold,
fontItalic: headerFontItalic,
fontUnderline: headerFontUnderline,
fontStrikethrough: headerFontStrikethrough
},
{
onSave: () => options.save(),
onRevert: () => options.revert(),
placeholder: use => use(hasMixedStyle) ? t('Mixed style') : t('Default header style')
}
);
}),
)];
}),
cssLine(
cssLabel(t('CELL STYLE')),
cssButton(
t('Open row styles'),
dom.on('click', allCommands.viewTabOpen.run),
dom.hide(!isTableWidget),
),
),
cssRow(
testId('cell-color-select'),
dom.domComputedOwned(fromKo(this._field.config.style), (holder, options) => {
const textColor = fromKo(options.prop("textColor"));
const fillColor = fromKo(options.prop("fillColor"));
const fontBold = fromKo(options.prop("fontBold"));
const fontUnderline = fromKo(options.prop("fontUnderline"));
const fontItalic = fromKo(options.prop("fontItalic"));
const fontStrikethrough = fromKo(options.prop("fontStrikethrough"));
const hasMixedStyle = Computed.create(holder, use => {
if (!use(this._field.config.multiselect)) { return false; }
const commonStyle = [
use(options.mixed('textColor')),
use(options.mixed('fillColor')),
use(options.mixed('fontBold')),
use(options.mixed('fontUnderline')),
use(options.mixed('fontItalic')),
use(options.mixed('fontStrikethrough'))
];
return commonStyle.some(Boolean);
});
return colorSelect(
{
textColor: new ColorOption({
color: textColor,
defaultColor: this._defaultTextColor,
noneText: 'default',
}),
fillColor: new ColorOption({
color: fillColor,
allowsNone: true,
noneText: 'none',
}),
fontBold: fontBold,
fontItalic: fontItalic,
fontUnderline: fontUnderline,
fontStrikethrough: fontStrikethrough
}, {
onSave: () => options.save(),
onRevert: () => options.revert(),
placeholder: use => use(hasMixedStyle) ? t('Mixed style') : t('Default cell style')
}
);
}),
),
dom.create(ConditionalStyle, t("Cell Style"), this._field, this._gristDoc, fromKo(this._field.config.multiselect))
];
}
}
const cssLine = styled('div', `
display: flex;
margin: 16px 16px 12px 16px;
justify-content: space-between;
align-items: baseline;
`);
const cssLabel = styled('div', `
color: ${theme.text};
text-transform: uppercase;
font-size: ${vars.xsmallFontSize};
`);
const cssButton = styled(textButton, `
font-size: ${vars.mediumFontSize};
`);
const cssRow = styled('div', `
display: flex;
margin: 8px 16px;
align-items: center;
&-top-space {
margin-top: 24px;
}
&-disabled {
color: ${theme.disabledText};
}
`);