Skip to content

Commit f90f25c

Browse files
authored
devex: Refactor syntax-input and link-selector-table into form controls (#2588)
Refactors `<btrix-syntax-input>` and `<btrix-link-selector-table>` to be form-associated controls.
1 parent 6afb317 commit f90f25c

File tree

23 files changed

+577
-263
lines changed

23 files changed

+577
-263
lines changed

frontend/src/components/ui/code.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { html as staticHtml, unsafeStatic } from "lit/static-html.js";
88
import { TailwindElement } from "@/classes/TailwindElement";
99
import { tw } from "@/utils/tailwind";
1010

11-
enum Language {
11+
export enum Language {
1212
Javascript = "javascript",
1313
XML = "xml",
1414
CSS = "css",

frontend/src/components/ui/config-details.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,10 @@ export class ConfigDetails extends BtrixElement {
503503
selectors.length
504504
? html`
505505
<div class="mb-2">
506-
<btrix-link-selector-table .selectors=${selectors}>
506+
<btrix-link-selector-table
507+
.selectors=${selectors}
508+
aria-readonly="true"
509+
>
507510
</btrix-link-selector-table>
508511
</div>
509512
`

frontend/src/components/ui/data-grid/controllers/rows.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,15 @@ export class DataGridRowsController implements ReactiveController {
6565
if (!this.#prevItems || items !== this.#prevItems) {
6666
this.setRowsFromItems(items);
6767

68-
// this.#host.requestUpdate();
69-
7068
this.#prevItems = items;
7169
}
7270
}
7371

72+
public updateItem<T extends GridItem = GridItem>(id: GridRowId, item: T) {
73+
this.rows.set(id, item);
74+
this.#host.requestUpdate();
75+
}
76+
7477
public addRows<T extends GridItem = GridItem>(
7578
defaultItem: T | EmptyObject = {},
7679
count = 1,

frontend/src/components/ui/data-grid/data-grid-cell.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import { ifDefined } from "lit/directives/if-defined.js";
66

77
import { TableCell } from "../table/table-cell";
88

9-
import type { GridColumn, GridColumnSelectType, GridItem } from "./types";
9+
import type {
10+
GridColumn,
11+
GridColumnSelectType,
12+
GridItem,
13+
GridItemValue,
14+
} from "./types";
1015
import { GridColumnType } from "./types";
1116

1217
import { DataGridFocusController } from "@/components/ui/data-grid/controllers/focus";
@@ -44,6 +49,9 @@ export class DataGridCell extends TableCell {
4449
@property({ type: Object })
4550
item?: GridItem;
4651

52+
@property({ type: String })
53+
value?: GridItemValue;
54+
4755
@property({ type: Boolean })
4856
editable = false;
4957

@@ -104,7 +112,7 @@ export class DataGridCell extends TableCell {
104112
if (!this.column || !this.item) return html`<slot></slot>`;
105113

106114
if (this.editable) {
107-
return this.renderEditCell({ item: this.item });
115+
return this.renderEditCell({ item: this.item, value: this.value });
108116
}
109117

110118
return this.renderCell({ item: this.item });
@@ -114,12 +122,18 @@ export class DataGridCell extends TableCell {
114122
return html`${(this.column && item[this.column.field]) ?? ""}`;
115123
};
116124

117-
renderEditCell = ({ item }: { item: GridItem }) => {
125+
renderEditCell = ({
126+
item,
127+
value: cellValue,
128+
}: {
129+
item: GridItem;
130+
value?: GridItemValue;
131+
}) => {
118132
const col = this.column;
119133

120134
if (!col) return html``;
121135

122-
const value = item[col.field] ?? "";
136+
const value = cellValue ?? item[col.field] ?? "";
123137

124138
switch (col.inputType) {
125139
case GridColumnType.Select: {

frontend/src/components/ui/data-grid/data-grid-row.ts

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import clsx from "clsx";
33
import { html, type PropertyValues } from "lit";
44
import { customElement, property, queryAll, state } from "lit/decorators.js";
55
import { directive } from "lit/directive.js";
6+
import { ifDefined } from "lit/directives/if-defined.js";
67
import isEqual from "lodash/fp/isEqual";
78

89
import { CellDirective } from "./cellDirective";
@@ -15,6 +16,7 @@ import type { GridColumn, GridItem, GridRowId } from "./types";
1516

1617
import { DataGridFocusController } from "@/components/ui/data-grid/controllers/focus";
1718
import { TableRow } from "@/components/ui/table/table-row";
19+
import { FormControl } from "@/mixins/FormControl";
1820
import { tw } from "@/utils/tailwind";
1921

2022
export type RowRemoveEventDetail = {
@@ -31,12 +33,7 @@ const editableCellStyle = tw`p-0 focus-visible:bg-slate-50 `;
3133
*/
3234
@customElement("btrix-data-grid-row")
3335
@localized()
34-
export class DataGridRow extends TableRow {
35-
// TODO Abstract to mixin or decorator
36-
// https://github.com/webrecorder/browsertrix/issues/2577
37-
static formAssociated = true;
38-
readonly #internals: ElementInternals;
39-
36+
export class DataGridRow extends FormControl(TableRow) {
4037
/**
4138
* Set of columns.
4239
*/
@@ -73,12 +70,6 @@ export class DataGridRow extends TableRow {
7370
@property({ type: String, reflect: true })
7471
name?: string;
7572

76-
/**
77-
* Make row focusable on validation.
78-
*/
79-
@property({ type: Number, reflect: true })
80-
tabindex = 0;
81-
8273
@state()
8374
private cellValues: Partial<GridItem> = {};
8475

@@ -89,44 +80,11 @@ export class DataGridRow extends TableRow {
8980
InputElement["validationMessage"]
9081
>();
9182

92-
public formAssociatedCallback() {
93-
console.debug("form associated");
94-
}
95-
9683
public formResetCallback() {
9784
this.setValue(this.item || {});
9885
this.commitValue();
9986
}
10087

101-
public formDisabledCallback(disabled: boolean) {
102-
console.debug("form disabled:", disabled);
103-
}
104-
105-
public formStateRestoreCallback(state: string | FormData, reason: string) {
106-
console.debug("formStateRestoreCallback:", state, reason);
107-
}
108-
109-
public checkValidity(): boolean {
110-
return this.#internals.checkValidity();
111-
}
112-
113-
public reportValidity(): void {
114-
this.#internals.reportValidity();
115-
}
116-
117-
public get validity(): ValidityState {
118-
return this.#internals.validity;
119-
}
120-
121-
public get validationMessage(): string {
122-
return this.#internals.validationMessage;
123-
}
124-
125-
constructor() {
126-
super();
127-
this.#internals = this.attachInternals();
128-
}
129-
13088
protected createRenderRoot() {
13189
const root = super.createRenderRoot();
13290

@@ -162,7 +120,7 @@ export class DataGridRow extends TableRow {
162120
this.cellValues[field] = cellValues[field];
163121
});
164122

165-
this.#internals.setFormValue(JSON.stringify(this.cellValues));
123+
this.setFormValue(JSON.stringify(this.cellValues));
166124
}
167125

168126
private commitValue() {
@@ -228,6 +186,7 @@ export class DataGridRow extends TableRow {
228186
)}
229187
.column=${col}
230188
.item=${this.item}
189+
value=${ifDefined(this.cellValues[col.field] ?? undefined)}
231190
?editable=${editable}
232191
${cell(col)}
233192
@keydown=${this.onKeydown}
@@ -337,7 +296,7 @@ export class DataGridRow extends TableRow {
337296
this.#invalidInputsMap.delete(field);
338297
} else {
339298
this.#invalidInputsMap.set(field, validationMessage);
340-
this.#internals.setValidity(validity, validationMessage, tableCell);
299+
this.setValidity(validity, validationMessage, tableCell);
341300
}
342301

343302
this.setValue({
@@ -357,7 +316,7 @@ export class DataGridRow extends TableRow {
357316
this.#invalidInputsMap.delete(field);
358317
} else {
359318
this.#invalidInputsMap.set(field, validationMessage);
360-
this.#internals.setValidity(validity, validationMessage, tableCell);
319+
this.setValidity(validity, validationMessage, tableCell);
361320
}
362321

363322
this.commitValue();
@@ -371,13 +330,13 @@ export class DataGridRow extends TableRow {
371330
);
372331

373332
if (firstInvalid?.validity && firstInvalid.validationMessage) {
374-
this.#internals.setValidity(
333+
this.setValidity(
375334
firstInvalid.validity,
376335
firstInvalid.validationMessage,
377336
firstInvalid,
378337
);
379338
} else {
380-
this.#internals.setValidity({});
339+
this.setValidity({});
381340
}
382341
}
383342
};

frontend/src/components/ui/data-grid/data-grid.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,6 @@ export class DataGrid extends TailwindElement {
127127
@property({ attribute: false })
128128
rowsController = new DataGridRowsController(this);
129129

130-
/**
131-
* Make grid focusable on validation.
132-
*/
133-
@property({ type: Number, reflect: true })
134-
tabindex = 0;
135-
136130
render() {
137131
if (!this.columns?.length) return;
138132

frontend/src/components/ui/data-grid/renderRows.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import type { GridItem, GridRowId, GridRows } from "./types";
55

66
export function renderRows<T = GridItem>(
77
rows: GridRows<GridItem>,
8-
renderRow: ({ id, item }: { id: GridRowId; item: T }) => TemplateResult,
8+
renderRow: (
9+
{ id, item }: { id: GridRowId; item: T },
10+
index: number,
11+
) => TemplateResult,
912
) {
1013
return repeat(
1114
rows,
1215
([id]) => id,
13-
([id, item]) => renderRow({ id, item: item as T }),
16+
([id, item], i) => renderRow({ id, item: item as T }, i),
1417
);
1518
}

frontend/src/components/ui/data-grid/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export type GridItem<T extends PropertyKey = string> = Record<
66
string | number | null | undefined
77
>;
88

9+
export type GridItemValue<T extends PropertyKey = string> =
10+
GridItem<T>[keyof GridItem<T>];
11+
912
export enum GridColumnType {
1013
Text = "text",
1114
Number = "number",
@@ -30,8 +33,11 @@ export type GridColumn<T = string> = {
3033
required?: boolean;
3134
inputPlaceholder?: string;
3235
width?: string;
33-
renderEditCell?: ({ item }: { item: GridItem }) => TemplateResult<1>;
34-
renderCell?: ({ item }: { item: GridItem }) => TemplateResult<1>;
36+
renderEditCell?: (props: {
37+
item: GridItem;
38+
value?: GridItem[keyof GridItem];
39+
}) => TemplateResult<1>;
40+
renderCell?: (props: { item: GridItem }) => TemplateResult<1>;
3541
} & (
3642
| {
3743
inputType?: GridColumnType;

frontend/src/components/ui/data-table.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { TailwindElement } from "@/classes/TailwindElement";
66
type CellContent = string | TemplateResult<1>;
77

88
/**
9+
* @deprecated Use `<btrix-data-grid>` instead.
10+
*
911
* Styled tables for handling lists of tabular data.
1012
* Data tables are less flexible than `<btrix-table>` but require less configuration.
1113
*/

0 commit comments

Comments
 (0)