@openmfp/webcomponents 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/.github/workflows/pipeline.yaml +41 -0
  2. package/.storybook/main.ts +34 -0
  3. package/.storybook/preview.ts +29 -0
  4. package/.storybook/tsconfig.json +11 -0
  5. package/AGENTS.md +153 -0
  6. package/CODEOWNERS +6 -0
  7. package/CONTRIBUTING.md +95 -0
  8. package/LICENSE +201 -0
  9. package/LICENSES/Apache-2.0.txt +73 -0
  10. package/README.md +91 -0
  11. package/REUSE.toml +9 -0
  12. package/angular.json +157 -0
  13. package/docs/dashboard.md +358 -0
  14. package/docs/declarative-form.md +178 -0
  15. package/docs/declarative-table-card.md +235 -0
  16. package/docs/declarative-table.md +315 -0
  17. package/eslint.config.js +41 -0
  18. package/package.json +73 -0
  19. package/projects/ngx/cards/favorites/favorites.component.html +12 -0
  20. package/projects/ngx/cards/favorites/favorites.component.scss +50 -0
  21. package/projects/ngx/cards/favorites/favorites.component.ts +19 -0
  22. package/projects/ngx/cards/public-api.ts +4 -0
  23. package/projects/ngx/cards/service-status/service-status-card.component.html +15 -0
  24. package/projects/ngx/cards/service-status/service-status-card.component.scss +87 -0
  25. package/projects/ngx/cards/service-status/service-status-card.component.ts +36 -0
  26. package/projects/ngx/cards/stories/visited-service-card.stories.ts +149 -0
  27. package/projects/ngx/cards/visited-service-card/visited-service-card.component.html +17 -0
  28. package/projects/ngx/cards/visited-service-card/visited-service-card.component.scss +34 -0
  29. package/projects/ngx/cards/visited-service-card/visited-service-card.component.ts +22 -0
  30. package/projects/ngx/cards/whats-new/whats-new.component.html +10 -0
  31. package/projects/ngx/cards/whats-new/whats-new.component.scss +25 -0
  32. package/projects/ngx/cards/whats-new/whats-new.component.ts +46 -0
  33. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.html +28 -0
  34. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.scss +44 -0
  35. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.spec.ts +85 -0
  36. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.ts +58 -0
  37. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.html +29 -0
  38. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.scss +63 -0
  39. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.spec.ts +255 -0
  40. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.ts +75 -0
  41. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.spec.ts +76 -0
  42. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.ts +109 -0
  43. package/projects/ngx/declarative-ui/dashboard/card/utils/index.ts +4 -0
  44. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.spec.ts +141 -0
  45. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.ts +44 -0
  46. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.spec.ts +142 -0
  47. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.ts +52 -0
  48. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.spec.ts +107 -0
  49. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.ts +22 -0
  50. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.html +134 -0
  51. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.scss +88 -0
  52. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.spec.ts +354 -0
  53. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.ts +238 -0
  54. package/projects/ngx/declarative-ui/dashboard/dashboard/index.ts +1 -0
  55. package/projects/ngx/declarative-ui/dashboard/index.ts +5 -0
  56. package/projects/ngx/declarative-ui/dashboard/models/constants.ts +2 -0
  57. package/projects/ngx/declarative-ui/dashboard/models/dashboard.model.ts +50 -0
  58. package/projects/ngx/declarative-ui/dashboard/models/index.ts +1 -0
  59. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.html +28 -0
  60. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.scss +85 -0
  61. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.spec.ts +104 -0
  62. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.ts +23 -0
  63. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.html +62 -0
  64. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.scss +12 -0
  65. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.spec.ts +301 -0
  66. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.ts +166 -0
  67. package/projects/ngx/declarative-ui/form/declarative-form/index.ts +1 -0
  68. package/projects/ngx/declarative-ui/form/index.ts +2 -0
  69. package/projects/ngx/declarative-ui/form/models/form-field-definition.ts +15 -0
  70. package/projects/ngx/declarative-ui/form/models/index.ts +1 -0
  71. package/projects/ngx/declarative-ui/form/utils/set-property-by-path.ts +30 -0
  72. package/projects/ngx/declarative-ui/models/index.ts +2 -0
  73. package/projects/ngx/declarative-ui/models/resource.ts +5 -0
  74. package/projects/ngx/declarative-ui/models/ui-definition.ts +95 -0
  75. package/projects/ngx/declarative-ui/public-api.ts +4 -0
  76. package/projects/ngx/declarative-ui/stories/add-card-dialog.stories.ts +91 -0
  77. package/projects/ngx/declarative-ui/stories/background-lightblue.png +0 -0
  78. package/projects/ngx/declarative-ui/stories/dashboard.cards.ts +107 -0
  79. package/projects/ngx/declarative-ui/stories/dashboard.stories.ts +296 -0
  80. package/projects/ngx/declarative-ui/stories/declarative-form.stories.ts +149 -0
  81. package/projects/ngx/declarative-ui/stories/declarative-table-card.stories.ts +358 -0
  82. package/projects/ngx/declarative-ui/stories/declarative-table.stories.ts +363 -0
  83. package/projects/ngx/declarative-ui/stories/pods-table.config.ts +188 -0
  84. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html +138 -0
  85. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss +21 -0
  86. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts +345 -0
  87. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts +61 -0
  88. package/projects/ngx/declarative-ui/table/declarative-table/index.ts +1 -0
  89. package/projects/ngx/declarative-ui/table/index.ts +2 -0
  90. package/projects/ngx/declarative-ui/table/models/index.ts +14 -0
  91. package/projects/ngx/declarative-ui/table/models/table.model.ts +17 -0
  92. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.spec.ts +146 -0
  93. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.ts +69 -0
  94. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.spec.ts +70 -0
  95. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.ts +13 -0
  96. package/projects/ngx/declarative-ui/table/utils/proccess-fields.spec.ts +511 -0
  97. package/projects/ngx/declarative-ui/table/utils/proccess-fields.ts +71 -0
  98. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.spec.ts +372 -0
  99. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.ts +98 -0
  100. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-cell.constants.ts +5 -0
  101. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.html +1 -0
  102. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.scss +0 -0
  103. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.spec.ts +119 -0
  104. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.ts +35 -0
  105. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.html +7 -0
  106. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.scss +0 -0
  107. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.spec.ts +114 -0
  108. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.ts +19 -0
  109. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.html +7 -0
  110. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.scss +10 -0
  111. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.spec.ts +188 -0
  112. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.ts +16 -0
  113. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.html +59 -0
  114. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.scss +33 -0
  115. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.spec.ts +316 -0
  116. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.ts +115 -0
  117. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.html +156 -0
  118. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.scss +123 -0
  119. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.spec.ts +786 -0
  120. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts +286 -0
  121. package/projects/ngx/declarative-ui/table-card/index.ts +2 -0
  122. package/projects/ngx/declarative-ui/table-card/models/configs.ts +46 -0
  123. package/projects/ngx/declarative-ui/tsconfig.lib.json +11 -0
  124. package/projects/ngx/declarative-ui/tsconfig.lib.prod.json +9 -0
  125. package/projects/ngx/declarative-ui/tsconfig.spec.json +9 -0
  126. package/projects/ngx/ng-package.json +8 -0
  127. package/projects/ngx/package.json +22 -0
  128. package/projects/ngx/public-api.ts +2 -0
  129. package/projects/ngx/tsconfig.lib.json +11 -0
  130. package/projects/ngx/tsconfig.lib.prod.json +9 -0
  131. package/projects/webcomponents/main.ts +92 -0
  132. package/projects/webcomponents/tsconfig.app.json +9 -0
  133. package/projects/webcomponents-dashboard/main.ts +15 -0
  134. package/projects/webcomponents-dashboard/tsconfig.app.json +9 -0
  135. package/renovate.json +6 -0
  136. package/scripts/bundle-wc.mjs +79 -0
  137. package/tsconfig.json +37 -0
  138. package/tsconfig.spec.json +8 -0
  139. package/tsconfig.storybook.json +16 -0
  140. package/vitest.config.ts +26 -0
@@ -0,0 +1,178 @@
1
+ # DeclarativeForm
2
+
3
+ A dynamic form web component that renders fields from a declarative field definition. The component does not execute validation logic. It emits per-field change events based on each field's `validation` strategy, accepts host-owned field errors, and emits a nested value only when the host triggers submit.
4
+
5
+ ## Tags
6
+
7
+ | Usage | Tag |
8
+ | ---------------------------------- | --------------------------- |
9
+ | Angular component | `<mfp-declarative-form>` |
10
+ | Web Component (framework-agnostic) | `<mfp-wc-declarative-form>` |
11
+
12
+ ---
13
+
14
+ ## Usage as a web component
15
+
16
+ ```html
17
+ <mfp-wc-declarative-form id="form"></mfp-wc-declarative-form>
18
+
19
+ <script type="module">
20
+ const form = document.getElementById('form');
21
+
22
+ form.fields = [
23
+ { name: 'metadata.name', label: 'Name', required: true, validation: 'onChange' },
24
+ { name: 'metadata.namespace', label: 'Namespace', validation: 'onBlur' },
25
+ ];
26
+
27
+ form.addEventListener('fieldChange', (event) => {
28
+ const { fieldProperty, value } = event.detail;
29
+ const fieldErrors = { ...form.fieldErrors };
30
+
31
+ if (fieldProperty === 'metadata.name' && !value) {
32
+ fieldErrors[fieldProperty] = 'Name is required';
33
+ } else {
34
+ fieldErrors[fieldProperty] = null;
35
+ }
36
+
37
+ form.fieldErrors = fieldErrors;
38
+ });
39
+
40
+ form.addEventListener('formSubmit', (event) => {
41
+ console.log(event.detail);
42
+ // { metadata: { name: 'my-app', namespace: 'default' } }
43
+ });
44
+
45
+ // Trigger when the surrounding page decides the form should submit.
46
+ form.submit();
47
+ </script>
48
+ ```
49
+
50
+ > `fields`, `initialValues`, and `fieldErrors` are JavaScript properties, not HTML attributes.
51
+
52
+ ---
53
+
54
+ ## Usage as an Angular component
55
+
56
+ ```ts
57
+ import {
58
+ DeclarativeForm,
59
+ FormFieldChangeEvent,
60
+ FormFieldDefinition,
61
+ FormFieldErrors,
62
+ } from '@openmfp/webcomponents';
63
+
64
+ @Component({
65
+ imports: [DeclarativeForm],
66
+ template: `
67
+ <mfp-declarative-form
68
+ #form
69
+ [fields]="fields"
70
+ [initialValues]="initialValues"
71
+ [fieldErrors]="fieldErrors"
72
+ (fieldChange)="onFieldChange($event)"
73
+ (formSubmit)="onSubmit($event)"
74
+ />
75
+
76
+ <button type="button" (click)="form.submit()">Save</button>
77
+ `,
78
+ })
79
+ export class MyComponent {
80
+ fields: FormFieldDefinition[] = [
81
+ { name: 'metadata.name', label: 'Name', required: true, validation: 'onChange' },
82
+ { name: 'metadata.namespace', label: 'Namespace', validation: 'onBlur' },
83
+ ];
84
+
85
+ initialValues = {
86
+ 'metadata.name': 'my-app',
87
+ 'metadata.namespace': 'default',
88
+ };
89
+
90
+ fieldErrors: FormFieldErrors = {};
91
+
92
+ onFieldChange(event: FormFieldChangeEvent): void {
93
+ const { fieldProperty, value } = event;
94
+ this.fieldErrors = {
95
+ ...this.fieldErrors,
96
+ [fieldProperty]: !value ? 'Field is required' : null,
97
+ };
98
+ }
99
+
100
+ onSubmit(value: Record<string, unknown>): void {
101
+ // value is nested: { metadata: { name: 'my-app', namespace: 'default' } }
102
+ }
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## API
109
+
110
+ ### Inputs
111
+
112
+ | Input | Type | Required | Default | Description |
113
+ | --------------- | ------------------------- | -------- | ------- | ----------------------------------------------------------------------------- |
114
+ | `fields` | `FormFieldDefinition[]` | yes | - | Field definitions to render |
115
+ | `initialValues` | `Record<string, unknown>` | no | `{}` | Initial values keyed by exact `field.name` |
116
+ | `fieldErrors` | `FormFieldErrors` | no | `{}` | Host-owned errors keyed by exact `field.name` |
117
+ | `editMode` | `boolean` | no | `false` | Signals edit mode to consumers; does not change component behavior on its own |
118
+
119
+ ### Outputs / Events
120
+
121
+ | Event | Detail payload | Description |
122
+ | ------------ | --------------------------- | ------------------------------------------------------------------ |
123
+ | `fieldChange` | `FormFieldChangeEvent` | Fires per-field based on the field's `validation` strategy |
124
+ | `formSubmit` | `Record<string, unknown>` | Fires when `submit()` is called; value is nested |
125
+
126
+ ---
127
+
128
+ ## Types
129
+
130
+ ```ts
131
+ interface FormFieldDefinition {
132
+ name: string; // Field key; dots create nested submit output paths
133
+ label?: string; // Display label shown above the field
134
+ required?: boolean; // Visual required marker only
135
+ values?: string[]; // Static select options
136
+ disabled?: boolean; // Disables the field
137
+ validation?: 'onBlur' | 'onChange'; // When to emit fieldChange for this field
138
+ }
139
+
140
+ interface FormFieldChangeEvent {
141
+ fieldProperty: string; // The field property name (matches field.name)
142
+ value: unknown; // Current value of the control
143
+ }
144
+
145
+ type FormFieldErrors = Record<string, string | null>;
146
+ ```
147
+
148
+ `fieldChange` emits a single field at a time:
149
+
150
+ ```ts
151
+ { fieldProperty: 'metadata.name', value: 'my-app' }
152
+ ```
153
+
154
+ `formSubmit` emits a nested object:
155
+
156
+ ```ts
157
+ {
158
+ metadata: {
159
+ name: 'my-app',
160
+ namespace: 'default',
161
+ },
162
+ }
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Validation
168
+
169
+ - The component never executes validators.
170
+ - `required` only renders the required marker on the label/input.
171
+ - The `validation` property on each field controls when `fieldChange` fires:
172
+ - `'onChange'` — fires on every value change.
173
+ - `'onBlur'` — fires when the field loses focus.
174
+ - Not set — no `fieldChange` event is emitted for that field.
175
+ - On initialization (and when `initialValues` changes), the component emits `fieldChange` for every field that has a `validation` strategy. This lets the host run validation immediately and disable the submit button before the user interacts.
176
+ - The host validates the received `FormFieldChangeEvent` and updates `fieldErrors`.
177
+ - A field shows `Negative` value state and the error message only when it is dirty or touched and `fieldErrors[field.name]` is a non-empty string.
178
+ - Empty, missing, or `null` errors render as no error.
@@ -0,0 +1,235 @@
1
+ # DeclarativeTableCard
2
+
3
+ A card component that wraps `mfp-declarative-table` and adds a header, search, and optional create/edit/delete dialogs. Form validation and async submit state are owned by the host application.
4
+
5
+ ## Tags
6
+
7
+ | Usage | Tag |
8
+ | ---------------------------------- | --------------------------------- |
9
+ | Angular component | `<mfp-declarative-table-card>` |
10
+ | Web Component (framework-agnostic) | `<mfp-wc-declarative-table-card>` |
11
+
12
+ ---
13
+
14
+ ## Usage as a web component
15
+
16
+ ```html
17
+ <mfp-wc-declarative-table-card id="card"></mfp-wc-declarative-table-card>
18
+
19
+ <script type="module">
20
+ const card = document.getElementById('card');
21
+
22
+ card.resources = [
23
+ {
24
+ id: '1',
25
+ metadata: { name: 'api-server', namespace: 'default' },
26
+ status: { phase: 'Running' },
27
+ },
28
+ ];
29
+
30
+ card.config = {
31
+ header: 'Pods',
32
+ tableConfig: {
33
+ fields: [
34
+ { label: 'Name', property: 'metadata.name' },
35
+ { label: 'Namespace', property: 'metadata.namespace' },
36
+ ],
37
+ },
38
+ createResourceFormConfig: {
39
+ fields: [
40
+ { name: 'metadata.name', label: 'Name', required: true },
41
+ { name: 'metadata.namespace', label: 'Namespace' },
42
+ ],
43
+ title: 'Create Pod',
44
+ confirmLabel: 'Create',
45
+ },
46
+ };
47
+
48
+ card.addEventListener('createFieldChange', (event) => {
49
+ const { fieldProperty, value } = event.detail;
50
+
51
+ card.createFormState = {
52
+ fieldErrors: {
53
+ ...card.createFormState?.fieldErrors,
54
+ [fieldProperty]: !value ? 'Field is required' : null,
55
+ },
56
+ };
57
+ });
58
+
59
+ card.addEventListener('createSubmit', async (event) => {
60
+ await createPod(event.detail);
61
+ card.createFormState = {};
62
+ card.closeCreateDialog();
63
+ });
64
+ </script>
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Usage as an Angular component
70
+
71
+ ```ts
72
+ import {
73
+ DeclarativeTableCard,
74
+ FormFieldChangeEvent,
75
+ TableCardConfig,
76
+ TableCardFormState,
77
+ } from '@openmfp/webcomponents';
78
+
79
+ @Component({
80
+ imports: [DeclarativeTableCard],
81
+ template: `
82
+ <mfp-declarative-table-card
83
+ #tableCard
84
+ [config]="config"
85
+ [resources]="pods"
86
+ [createFormState]="createFormState"
87
+ [editFormState]="editFormState"
88
+ (createFieldChange)="onCreateFieldChange($event)"
89
+ (editFieldChange)="onEditFieldChange($event)"
90
+ (createSubmit)="onCreateSubmit($event, tableCard)"
91
+ (editSubmit)="onEditSubmit($event, tableCard)"
92
+ (deleteSubmit)="onDeleteSubmit($event, tableCard)"
93
+ (searchChanged)="onSearch($event)"
94
+ />
95
+ `,
96
+ })
97
+ export class MyComponent {
98
+ pods = [];
99
+ createFormState: TableCardFormState = {};
100
+ editFormState: TableCardFormState = {};
101
+
102
+ config: TableCardConfig = {
103
+ header: 'Pods',
104
+ tableConfig: {
105
+ fields: [
106
+ { label: 'Name', property: 'metadata.name' },
107
+ { label: 'Namespace', property: 'metadata.namespace' },
108
+ ],
109
+ },
110
+ createResourceFormConfig: {
111
+ fields: [
112
+ { name: 'metadata.name', label: 'Name', required: true },
113
+ { name: 'metadata.namespace', label: 'Namespace' },
114
+ ],
115
+ title: 'Create Pod',
116
+ confirmLabel: 'Create',
117
+ },
118
+ deleteResourceConfirmationConfig: {
119
+ title: 'Delete Pod?',
120
+ message: 'This action cannot be undone.',
121
+ confirmLabel: 'Delete',
122
+ },
123
+ };
124
+
125
+ onCreateFieldChange(event: FormFieldChangeEvent): void {
126
+ const { fieldProperty, value } = event;
127
+ this.createFormState = {
128
+ fieldErrors: {
129
+ ...this.createFormState.fieldErrors,
130
+ [fieldProperty]: !value ? 'Field is required' : null,
131
+ },
132
+ };
133
+ }
134
+
135
+ onEditFieldChange(event: {
136
+ resource: Pod;
137
+ formChangeEvent: FormFieldChangeEvent;
138
+ }): void {
139
+ // Validate event.formChangeEvent and update editFormState.
140
+ }
141
+
142
+ async onCreateSubmit(
143
+ value: Record<string, unknown>,
144
+ tableCard: DeclarativeTableCard<Pod>,
145
+ ): Promise<void> {
146
+ await this.createPod(value);
147
+ this.createFormState = {};
148
+ tableCard.closeCreateDialog();
149
+ }
150
+
151
+ async onEditSubmit(
152
+ event: { resource: Pod; value: Record<string, unknown> },
153
+ tableCard: DeclarativeTableCard<Pod>,
154
+ ): Promise<void> {
155
+ await this.updatePod(event.resource, event.value);
156
+ this.editFormState = {};
157
+ tableCard.closeEditDialog();
158
+ }
159
+
160
+ async onDeleteSubmit(
161
+ pod: Pod,
162
+ tableCard: DeclarativeTableCard<Pod>,
163
+ ): Promise<void> {
164
+ await this.deletePod(pod);
165
+ tableCard.closeDeleteDialog();
166
+ }
167
+ }
168
+ ```
169
+
170
+ ---
171
+
172
+ ## API
173
+
174
+ ### Inputs
175
+
176
+ | Input | Type | Required | Default | Description |
177
+ | ----------------- | -------------------- | -------- | ------- | --------------------------------------------------------- |
178
+ | `resources` | `T[]` | yes | - | Data rows passed to the inner table |
179
+ | `config` | `TableCardConfig` | yes | - | Static table, button, and dialog configuration |
180
+ | `createFormState` | `TableCardFormState` | no | `{}` | Runtime validation and submit state for the create dialog |
181
+ | `editFormState` | `TableCardFormState` | no | `{}` | Runtime validation and submit state for the edit dialog |
182
+
183
+ ### Outputs / Events
184
+
185
+ | Event | Payload | Description |
186
+ | ------------------------ | ------------------------------------------------- | ------------------------------------------------------------ |
187
+ | `createFieldChange` | `FormFieldChangeEvent` | Re-emits per-field change from the create form |
188
+ | `editFieldChange` | `{ resource: T; formChangeEvent: FormFieldChangeEvent }` | Re-emits per-field change from the edit form with resource |
189
+ | `createSubmit` | `Record<string, unknown>` | Fires when the create dialog Save button is clicked |
190
+ | `editSubmit` | `{ resource: T; value: Record<string, unknown> }` | Fires when the edit dialog Save button is clicked |
191
+ | `deleteSubmit` | `T` | Fires when the delete dialog Delete button is clicked |
192
+ | `searchChanged` | `string` | Emits 300 ms after the search input changes |
193
+ | `tableRowClicked` | `T` | Emits when a table row is clicked |
194
+ | `loadMoreResources` | - | Emits when the user triggers load more |
195
+ | `paginationLimitChanged` | `number` | Emits when the user changes page size |
196
+ | `actionButtonClick` | `ValueCellButtonClickEvent<T>` | Emits for row-action buttons other than built-in edit/delete |
197
+
198
+ ### Methods
199
+
200
+ | Method | Description |
201
+ | --------------------- | ------------------------ |
202
+ | `closeCreateDialog()` | Closes the create dialog |
203
+ | `closeEditDialog()` | Closes the edit dialog |
204
+ | `closeDeleteDialog()` | Closes the delete dialog |
205
+
206
+ Submit events do not close dialogs automatically. Close the dialog after successful validation, save, or delete.
207
+
208
+ ---
209
+
210
+ ## Configuration types
211
+
212
+ ```ts
213
+ interface TableCardConfig {
214
+ header: string;
215
+ headerTooltip?: string;
216
+ tableConfig: TableConfig;
217
+ buttonSettings?: TableCardButtonSettings;
218
+ createResourceFormConfig?: ResourceFormConfig;
219
+ editResourceFormConfig?: ResourceFormConfig;
220
+ deleteResourceConfirmationConfig?: DeleteResourceConfirmationConfig;
221
+ }
222
+
223
+ interface ResourceFormConfig {
224
+ fields: FormFieldDefinition[];
225
+ title?: string;
226
+ confirmLabel?: string;
227
+ cancelLabel?: string;
228
+ }
229
+
230
+ interface TableCardFormState {
231
+ fieldErrors?: FormFieldErrors;
232
+ }
233
+ ```
234
+
235
+ `ResourceFormConfig` is static. Keep runtime errors in `createFormState` / `editFormState`. The submit button is disabled when any entry in `fieldErrors` is truthy.