mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-20 01:02:22 +00:00
418681915e
Summary: - Forms now have a reset button. - Choice and Reference fields in forms now have an improved select menu. - Formula and attachments column types are no longer mappable or visible in forms. - Fields in a form widget are now removed if their column is deleted. - The preview button in a published form widget has been replaced with a view button. It now opens the published form in a new tab. - A new share menu for published form widgets, with options to copy a link or embed code. - Forms can now have multiple sections. - Form widgets now indicate when publishing is unavailable (e.g. in forks or unsaved documents). - General improvements to form styling. Test Plan: Browser tests. Reviewers: jarek Reviewed By: jarek Subscribers: paulfitz Differential Revision: https://phab.getgrist.com/D4203
100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import {FormLayoutNode} from 'app/client/components/FormRenderer';
|
|
import {buildEditor} from 'app/client/components/Forms/Editor';
|
|
import {BoxModel} from 'app/client/components/Forms/Model';
|
|
import * as css from 'app/client/components/Forms/styles';
|
|
import {textarea} from 'app/client/ui/inputs';
|
|
import {theme} from 'app/client/ui2018/cssVars';
|
|
import {not} from 'app/common/gutil';
|
|
import {Computed, dom, Observable, styled} from 'grainjs';
|
|
import {v4 as uuidv4} from 'uuid';
|
|
|
|
export class ParagraphModel extends BoxModel {
|
|
public edit = Observable.create(this, false);
|
|
|
|
protected defaultValue = '**Lorem** _ipsum_ dolor';
|
|
protected cssClass = '';
|
|
|
|
private _overlay = Computed.create(this, not(this.selected));
|
|
|
|
public override render(): HTMLElement {
|
|
const box = this;
|
|
const editMode = box.edit;
|
|
let element: HTMLElement;
|
|
const text = this.prop('text', this.defaultValue) as Observable<string|undefined>;
|
|
|
|
// There is a spacial hack here. We might be created as a separator component, but the rendering
|
|
// for separator looks bad when it is the only content, so add a special case for that.
|
|
const isSeparator = Computed.create(this, (use) => use(text) === '---');
|
|
|
|
return buildEditor({
|
|
box: this,
|
|
overlay: this._overlay,
|
|
editMode,
|
|
content: css.cssMarkdownRendered(
|
|
css.markdown(use => use(text) || '', dom.hide(editMode)),
|
|
dom.maybe(use => !use(text) && !use(editMode), () => cssEmpty('(empty)')),
|
|
css.cssMarkdownRendered.cls('-separator', isSeparator),
|
|
dom.on('click', () => {
|
|
if (!editMode.get() && this.selected.get()) {
|
|
editMode.set(true);
|
|
}
|
|
}),
|
|
css.cssMarkdownRendered.cls('-edit', editMode),
|
|
css.cssMarkdownRendered.cls(u => `-alignment-${u(box.prop('alignment', 'left'))}`),
|
|
this.cssClass ? dom.cls(this.cssClass, not(editMode)) : null,
|
|
dom.maybe(editMode, () => {
|
|
const draft = Observable.create(null, text.get() || '');
|
|
setTimeout(() => element?.focus(), 10);
|
|
return [
|
|
element = cssTextArea(draft, {autoGrow: true, onInput: true},
|
|
cssTextArea.cls('-edit', editMode),
|
|
css.saveControls(editMode, (ok) => {
|
|
if (ok && editMode.get()) {
|
|
text.set(draft.get());
|
|
this.save().catch(reportError);
|
|
}
|
|
})
|
|
),
|
|
];
|
|
}),
|
|
)
|
|
});
|
|
}
|
|
}
|
|
|
|
export function Paragraph(text: string, alignment?: 'left'|'right'|'center'): FormLayoutNode {
|
|
return {id: uuidv4(), type: 'Paragraph', text, alignment};
|
|
}
|
|
|
|
const cssTextArea = styled(textarea, `
|
|
color: ${theme.inputFg};
|
|
background-color: ${theme.mainPanelBg};
|
|
border: 0px;
|
|
width: 100%;
|
|
padding: 3px 6px;
|
|
outline: none;
|
|
max-height: 300px;
|
|
min-height: calc(3em * 1.5);
|
|
resize: none;
|
|
border-radius: 3px;
|
|
&-edit {
|
|
cursor: auto;
|
|
background: ${theme.inputBg};
|
|
outline: 2px solid black;
|
|
outline-offset: 1px;
|
|
border-radius: 2px;
|
|
}
|
|
&::placeholder {
|
|
color: ${theme.inputPlaceholderFg};
|
|
}
|
|
&[readonly] {
|
|
background-color: ${theme.inputDisabledBg};
|
|
color: ${theme.inputDisabledFg};
|
|
}
|
|
`);
|
|
|
|
const cssEmpty = styled('div', `
|
|
color: ${theme.inputPlaceholderFg};
|
|
font-style: italic;
|
|
`);
|