gristlabs_grist-core/app/client/ui/DescriptionConfig.ts

122 lines
3.0 KiB
TypeScript
Raw Permalink Normal View History

import {makeT} from 'app/client/lib/localization';
import {KoSaveableObservable} from 'app/client/models/modelUtil';
import {autoGrow} from 'app/client/ui/forms';
import {textarea, textInput} from 'app/client/ui/inputs';
import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
import {testId, theme} from 'app/client/ui2018/cssVars';
import {CursorPos} from 'app/plugin/GristAPI';
import {dom, DomArg, fromKo, MultiHolder, styled} from 'grainjs';
const t = makeT('DescriptionConfig');
export function buildDescriptionConfig(
owner: MultiHolder,
description: KoSaveableObservable<string>,
options: {
cursor: ko.Computed<CursorPos>,
testPrefix: string,
},
) {
// We will listen to cursor position and force a blur event on
// the text input, which will trigger save before the column observable
// will change its value.
// Otherwise, blur will be invoked after column change and save handler will
// update a different column.
let editor: HTMLTextAreaElement | undefined;
owner.autoDispose(
options.cursor.subscribe(() => {
editor?.blur();
})
);
return [
cssLabel(t("DESCRIPTION")),
cssRow(
editor = cssTextArea(fromKo(description),
{ onInput: false },
{ rows: '3' },
dom.on('blur', async (e, elem) => {
await description.saveOnly(elem.value);
}),
testId(`${options.testPrefix}-description`),
autoGrow(fromKo(description))
)
),
];
}
/**
* A generic version of buildDescriptionConfig that can be used for any text input.
*/
export function buildTextInput(
owner: MultiHolder,
options: {
label: string,
value: KoSaveableObservable<any>,
cursor?: ko.Computed<CursorPos>,
placeholder?: ko.Computed<string>,
},
...args: DomArg[]
) {
if (options.cursor) {
owner.autoDispose(
options.cursor.subscribe(() => {
options.value.save().catch(reportError);
})
);
}
return [
cssLabel(options.label),
cssRow(
cssTextInput(fromKo(options.value),
dom.on('blur', () => {
return options.value.save();
}),
dom.prop('placeholder', options.placeholder || ''),
...args
),
),
];
}
const cssTextInput = styled(textInput, `
color: ${theme.inputFg};
background-color: ${theme.mainPanelBg};
border: 1px solid ${theme.inputBorder};
width: 100%;
outline: none;
height: 28px;
border-radius: 3px;
padding: 0px 6px;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
&[readonly] {
background-color: ${theme.inputDisabledBg};
color: ${theme.inputDisabledFg};
}
`);
const cssTextArea = styled(textarea, `
color: ${theme.inputFg};
background-color: ${theme.mainPanelBg};
border: 1px solid ${theme.inputBorder};
width: 100%;
outline: none;
border-radius: 3px;
padding: 3px 7px;
min-height: calc(3em * 1.5);
resize: none;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
&[readonly] {
background-color: ${theme.inputDisabledBg};
color: ${theme.inputDisabledFg};
}
`);