mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Enable Record Cards
Summary: Adds remaining functionality, fixes, and polish to Record Cards and removes their feature flag, enabling them by default. Test Plan: Tests deferred; will be included in a follow-up diff. Reviewers: jarek, paulfitz Reviewed By: jarek Subscribers: paulfitz, jarek Differential Revision: https://phab.getgrist.com/D4121
This commit is contained in:
@@ -2,7 +2,6 @@ import {makeT} from 'app/client/lib/localization';
|
||||
import {DataRowModel} from 'app/client/models/DataRowModel';
|
||||
import {TableRec} from 'app/client/models/DocModel';
|
||||
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||
import {RECORD_CARDS} from 'app/client/models/features';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
|
||||
import {hideInPrintView, testId, theme} from 'app/client/ui2018/cssVars';
|
||||
@@ -20,7 +19,7 @@ const t = makeT('Reference');
|
||||
* Reference - The widget for displaying references to another table's records.
|
||||
*/
|
||||
export class Reference extends NTextBox {
|
||||
private _refTable: Computed<TableRec | null>;
|
||||
protected _refTable: Computed<TableRec | null>;
|
||||
private _visibleColRef: Computed<number>;
|
||||
private _validCols: Computed<Array<IOptionFull<number>>>;
|
||||
|
||||
@@ -120,23 +119,20 @@ export class Reference extends NTextBox {
|
||||
dom.cls('text_wrapping', this.wrapping),
|
||||
cssRefIcon('FieldReference',
|
||||
cssRefIcon.cls('-view-as-card', use =>
|
||||
RECORD_CARDS() && use(referenceId) !== 0 && use(formattedValue).hasRecordCard),
|
||||
use(referenceId) !== 0 && use(formattedValue).hasRecordCard),
|
||||
dom.on('click', async () => {
|
||||
if (!RECORD_CARDS()) { return; }
|
||||
if (referenceId.get() === 0 || !formattedValue.get().hasRecordCard) { return; }
|
||||
|
||||
const rowId = referenceId.get() as UIRowId;
|
||||
const sectionId = this._refTable.get()?.recordCardViewSectionRef();
|
||||
if (sectionId === undefined) {
|
||||
throw new Error('Unable to find Record Card section');
|
||||
throw new Error('Unable to open Record Card: undefined section id');
|
||||
}
|
||||
|
||||
const anchorUrlState = {hash: {rowId, sectionId, recordCard: true}};
|
||||
await urlState().pushUrl(anchorUrlState, {replace: true});
|
||||
}),
|
||||
dom.on('mousedown', (ev) => {
|
||||
if (!RECORD_CARDS()) { return; }
|
||||
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {DataRowModel} from 'app/client/models/DataRowModel';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import {testId, theme} from 'app/client/ui2018/cssVars';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
import {isList} from 'app/common/gristTypes';
|
||||
import {dom} from 'grainjs';
|
||||
import {Computed, dom, styled} from 'grainjs';
|
||||
import {cssChoiceList, cssToken} from "app/client/widgets/ChoiceListCell";
|
||||
import {Reference} from "app/client/widgets/Reference";
|
||||
import {choiceToken} from "app/client/widgets/ChoiceToken";
|
||||
@@ -10,24 +12,33 @@ import {choiceToken} from "app/client/widgets/ChoiceToken";
|
||||
* ReferenceList - The widget for displaying lists of references to another table's records.
|
||||
*/
|
||||
export class ReferenceList extends Reference {
|
||||
private _hasRecordCard = Computed.create(this, (use) => {
|
||||
const table = use(this._refTable);
|
||||
if (!table) { return false; }
|
||||
|
||||
return !use(use(table.recordCardViewSection).disabled);
|
||||
});
|
||||
|
||||
public buildDom(row: DataRowModel) {
|
||||
return cssChoiceList(
|
||||
dom.cls('field_clip'),
|
||||
cssChoiceList.cls('-wrap', this.wrapping),
|
||||
dom.style('justify-content', use => use(this.alignment) === 'right' ? 'flex-end' : use(this.alignment)),
|
||||
dom.domComputed((use) => {
|
||||
|
||||
if (use(row._isAddRow) || this.isDisposed() || use(this.field.displayColModel).isDisposed()) {
|
||||
// Work around JS errors during certain changes (noticed when visibleCol field gets removed
|
||||
// for a column using per-field settings).
|
||||
return null;
|
||||
}
|
||||
const value = row.cells[use(use(this.field.displayColModel).colId)];
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
const content = use(value);
|
||||
if (!content) { return null; }
|
||||
|
||||
const valueObs = row.cells[use(this.field.colId)];
|
||||
const value = valueObs && use(valueObs);
|
||||
if (!value) { return null; }
|
||||
|
||||
const displayValueObs = row.cells[use(use(this.field.displayColModel).colId)];
|
||||
const displayValue = displayValueObs && use(displayValueObs);
|
||||
if (!displayValue) { return null; }
|
||||
|
||||
// TODO: Figure out what the implications of this block are for ReferenceList.
|
||||
// if (isVersions(content)) {
|
||||
// // We can arrive here if the reference value is unchanged (viewed as a foreign key)
|
||||
@@ -36,20 +47,51 @@ export class ReferenceList extends Reference {
|
||||
// // just showing one version of the cell. TODO: elaborate.
|
||||
// return use(this._formatValue)(content[1].local || content[1].parent);
|
||||
// }
|
||||
const items = isList(content) ? content.slice(1) : [content];
|
||||
const values = isList(value) ? value.slice(1) : [value];
|
||||
const displayValues = isList(displayValue) ? displayValue.slice(1) : [displayValue];
|
||||
// Use field.visibleColFormatter instead of field.formatter
|
||||
// because we're formatting each list element to render tokens, not the whole list.
|
||||
const formatter = use(this.field.visibleColFormatter);
|
||||
return items.map(item => formatter.formatAny(item));
|
||||
return values.map((referenceId, i) => {
|
||||
return {
|
||||
referenceId,
|
||||
formattedValue: formatter.formatAny(displayValues[i]),
|
||||
};
|
||||
});
|
||||
},
|
||||
(input) => {
|
||||
if (!input) {
|
||||
(values) => {
|
||||
if (!values) {
|
||||
return null;
|
||||
}
|
||||
return input.map(token => {
|
||||
const isBlankReference = token.trim() === '';
|
||||
return values.map(({referenceId, formattedValue}) => {
|
||||
const isBlankReference = formattedValue.trim() === '';
|
||||
return choiceToken(
|
||||
isBlankReference ? '[Blank]' : token,
|
||||
[
|
||||
cssRefIcon('FieldReference',
|
||||
cssRefIcon.cls('-view-as-card', use =>
|
||||
referenceId !== 0 && use(this._hasRecordCard)),
|
||||
dom.on('click', async () => {
|
||||
if (referenceId === 0 || !this._hasRecordCard.get()) { return; }
|
||||
|
||||
const rowId = referenceId as number;
|
||||
const sectionId = this._refTable.get()?.recordCardViewSectionRef();
|
||||
if (sectionId === undefined) {
|
||||
throw new Error('Unable to open Record Card: undefined section id');
|
||||
}
|
||||
|
||||
const anchorUrlState = {hash: {rowId, sectionId, recordCard: true}};
|
||||
await urlState().pushUrl(anchorUrlState, {replace: true});
|
||||
}),
|
||||
dom.on('mousedown', (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}),
|
||||
),
|
||||
cssLabel(isBlankReference ? '[Blank]' : formattedValue,
|
||||
testId('ref-list-cell-token-label'),
|
||||
),
|
||||
dom.cls(cssRefIconAndLabel.className),
|
||||
],
|
||||
{
|
||||
blank: isBlankReference,
|
||||
},
|
||||
@@ -61,3 +103,26 @@ export class ReferenceList extends Reference {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const cssRefIcon = styled(icon, `
|
||||
--icon-color: ${theme.lightText};
|
||||
flex-shrink: 0;
|
||||
|
||||
&-view-as-card {
|
||||
cursor: pointer;
|
||||
}
|
||||
&-view-as-card:hover {
|
||||
--icon-color: ${theme.controlFg};
|
||||
}
|
||||
`);
|
||||
|
||||
const cssRefIconAndLabel = styled('div', `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`);
|
||||
|
||||
const cssLabel = styled('div', `
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user