mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Form kanban tasks
Summary: - Open all links in a new tab - Excluding not filled columns (to fix trigger formulas) - Fixed Ref/RefList submission - Removing redundant type definitions for Box - Adding header menu item - Default empty values in select control Test Plan: Updated Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D4166
This commit is contained in:
@@ -3,7 +3,7 @@ import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
import {reportError} from 'app/client/models/AppModel';
|
||||
import {ColumnRec, TableRec, ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {GRIST_FORMS_FEATURE, PERMITTED_CUSTOM_WIDGETS} from "app/client/models/features";
|
||||
import {PERMITTED_CUSTOM_WIDGETS} from "app/client/models/features";
|
||||
import {GristTooltips} from 'app/client/ui/GristTooltips';
|
||||
import {linkId, NoLink} from 'app/client/ui/selectBy';
|
||||
import {overflowTooltip, withInfoTooltip} from 'app/client/ui/tooltips';
|
||||
@@ -98,21 +98,17 @@ export interface IOptions extends ISelectOptions {
|
||||
|
||||
const testId = makeTestId('test-wselect-');
|
||||
|
||||
function maybeForms(): Array<'form'> {
|
||||
return GRIST_FORMS_FEATURE() ? ['form'] : [];
|
||||
}
|
||||
|
||||
// The picker disables some choices that do not make much sense. This function return the list of
|
||||
// compatible types given the tableId and whether user is creating a new page or not.
|
||||
function getCompatibleTypes(tableId: TableRef, isNewPage: boolean|undefined): IWidgetType[] {
|
||||
if (tableId !== 'New Table') {
|
||||
return ['record', 'single', 'detail', 'chart', 'custom', 'custom.calendar', ...maybeForms()];
|
||||
return ['record', 'single', 'detail', 'chart', 'custom', 'custom.calendar', 'form'];
|
||||
} else if (isNewPage) {
|
||||
// New view + new table means we'll be switching to the primary view.
|
||||
return ['record', ...maybeForms()];
|
||||
return ['record', 'form'];
|
||||
} else {
|
||||
// The type 'chart' makes little sense when creating a new table.
|
||||
return ['record', 'single', 'detail', ...maybeForms()];
|
||||
return ['record', 'single', 'detail', 'form'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +271,7 @@ const permittedCustomWidgets: IAttachedCustomWidget[] = PERMITTED_CUSTOM_WIDGETS
|
||||
const finalListOfCustomWidgetToShow = permittedCustomWidgets.filter(a=>
|
||||
registeredCustomWidgets.includes(a));
|
||||
const sectionTypes: IWidgetType[] = [
|
||||
'record', 'single', 'detail', ...maybeForms(), 'chart', ...finalListOfCustomWidgetToShow, 'custom'
|
||||
'record', 'single', 'detail', 'form', 'chart', ...finalListOfCustomWidgetToShow, 'custom'
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ import {
|
||||
MultiHolder,
|
||||
Observable,
|
||||
styled,
|
||||
subscribe
|
||||
subscribe,
|
||||
toKo
|
||||
} from 'grainjs';
|
||||
import * as ko from 'knockout';
|
||||
|
||||
@@ -955,12 +956,25 @@ export class RightPanel extends Disposable {
|
||||
return vsi && vsi.activeFieldBuilder();
|
||||
}));
|
||||
|
||||
const formView = owner.autoDispose(ko.computed(() => {
|
||||
// Sorry for the acrobatics below, but grainjs are not reentred when the active section changes.
|
||||
const viewInstance = owner.autoDispose(ko.computed(() => {
|
||||
const vsi = this._gristDoc.viewModel.activeSection?.().viewInstance();
|
||||
return (vsi ?? null) as FormView|null;
|
||||
if (!vsi || vsi.isDisposed() || !toKo(ko, this._isForm)) { return null; }
|
||||
return vsi;
|
||||
}));
|
||||
|
||||
const selectedBox = Computed.create(owner, (use) => use(formView) && use(use(formView)!.selectedBox));
|
||||
const formView = owner.autoDispose(ko.computed(() => {
|
||||
const view = viewInstance() as unknown as FormView;
|
||||
if (!view || !view.selectedBox) { return null; }
|
||||
return view;
|
||||
}));
|
||||
|
||||
const selectedBox = owner.autoDispose(ko.pureComputed(() => {
|
||||
const view = formView();
|
||||
if (!view) { return null; }
|
||||
const box = toKo(ko, view.selectedBox)();
|
||||
return box;
|
||||
}));
|
||||
const selectedField = Computed.create(owner, (use) => {
|
||||
const box = use(selectedBox);
|
||||
if (!box) { return null; }
|
||||
@@ -983,33 +997,38 @@ export class RightPanel extends Disposable {
|
||||
}
|
||||
});
|
||||
|
||||
return cssSection(
|
||||
return domAsync(imports.loadViewPane().then(() => buildConfigContainer(cssSection(
|
||||
// Field config.
|
||||
dom.maybe(selectedField, (field) => {
|
||||
dom.maybeOwned(selectedField, (scope, field) => {
|
||||
const requiredField = field.widgetOptionsJson.prop('formRequired');
|
||||
// V2 thing.
|
||||
// const hiddenField = field.widgetOptionsJson.prop('formHidden');
|
||||
const defaultField = field.widgetOptionsJson.prop('formDefault');
|
||||
const toComputed = (obs: typeof defaultField) => {
|
||||
const result = Computed.create(null, (use) => use(obs));
|
||||
const result = Computed.create(scope, (use) => use(obs));
|
||||
result.onWrite(val => obs.setAndSave(val));
|
||||
return result;
|
||||
};
|
||||
const fieldTitle = field.widgetOptionsJson.prop('question');
|
||||
|
||||
return [
|
||||
cssLabel(t("Field title")),
|
||||
cssRow(
|
||||
cssTextInput(
|
||||
fromKo(field.label),
|
||||
(val) => field.displayLabel.saveOnly(val),
|
||||
fromKo(fieldTitle),
|
||||
(val) => fieldTitle.saveOnly(val).catch(reportError),
|
||||
dom.prop('readonly', use => use(field.disableModify)),
|
||||
dom.prop('placeholder', use => use(field.displayLabel) || use(field.colId)),
|
||||
testId('field-title'),
|
||||
),
|
||||
),
|
||||
cssLabel(t("Table column name")),
|
||||
cssRow(
|
||||
cssTextInput(
|
||||
fromKo(field.colId),
|
||||
(val) => field.column().colId.saveOnly(val),
|
||||
fromKo(field.displayLabel),
|
||||
(val) => field.displayLabel.saveOnly(val).catch(reportError),
|
||||
dom.prop('readonly', use => use(field.disableModify)),
|
||||
testId('field-label'),
|
||||
),
|
||||
),
|
||||
// TODO: this is for V1 as it requires full cell editor here.
|
||||
@@ -1038,7 +1057,11 @@ export class RightPanel extends Disposable {
|
||||
]),
|
||||
cssSeparator(),
|
||||
cssLabel(t("Field rules")),
|
||||
cssRow(labeledSquareCheckbox(toComputed(requiredField), t("Required field")),),
|
||||
cssRow(labeledSquareCheckbox(
|
||||
toComputed(requiredField),
|
||||
t("Required field"),
|
||||
testId('field-required'),
|
||||
)),
|
||||
// V2 thing
|
||||
// cssRow(labeledSquareCheckbox(toComputed(hiddenField), t("Hidden field")),),
|
||||
];
|
||||
@@ -1071,7 +1094,7 @@ export class RightPanel extends Disposable {
|
||||
dom.maybe(u => !u(selectedColumn) && !u(selectedBox), () => [
|
||||
cssLabel(t('Layout')),
|
||||
])
|
||||
);
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import {hooks} from 'app/client/Hooks';
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
import {allCommands} from 'app/client/components/commands';
|
||||
import {ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {GRIST_FORMS_FEATURE} from 'app/client/models/features';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {menuDivider, menuItemCmd, menuItemLink} from 'app/client/ui2018/menus';
|
||||
@@ -96,7 +95,7 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool
|
||||
menuItemCmd(allCommands.viewTabOpen, t("Widget options"), testId('widget-options')),
|
||||
menuItemCmd(allCommands.sortFilterTabOpen, t("Advanced Sort & Filter"), dom.hide(viewSection.isRecordCard)),
|
||||
menuItemCmd(allCommands.dataSelectionTabOpen, t("Data selection"), dom.hide(viewSection.isRecordCard)),
|
||||
!GRIST_FORMS_FEATURE() ? null : menuItemCmd(allCommands.createForm, t("Create a form"), dom.show(isTable)),
|
||||
menuItemCmd(allCommands.createForm, t("Create a form"), dom.show(isTable)),
|
||||
]),
|
||||
|
||||
menuDivider(dom.hide(viewSection.isRecordCard)),
|
||||
|
||||
Reference in New Issue
Block a user