@happyvertical/smrt-fields 0.40.60 → 0.40.61

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 (184) hide show
  1. package/AGENTS.md +58 -12
  2. package/README.md +90 -2
  3. package/dist/__smrt-register__.d.ts +2 -0
  4. package/dist/__smrt-register__.d.ts.map +1 -0
  5. package/dist/cache.d.ts +18 -0
  6. package/dist/cache.d.ts.map +1 -0
  7. package/dist/collections/FieldPolicyCollection.d.ts +95 -0
  8. package/dist/collections/FieldPolicyCollection.d.ts.map +1 -0
  9. package/dist/field-definitions.d.ts +77 -0
  10. package/dist/field-definitions.d.ts.map +1 -0
  11. package/dist/field-policy-resolver.d.ts +46 -0
  12. package/dist/field-policy-resolver.d.ts.map +1 -0
  13. package/dist/index.d.ts +9 -532
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +752 -55
  16. package/dist/index.js.map +1 -1
  17. package/dist/manifest.json +45 -3
  18. package/dist/models/FieldPolicy.d.ts +163 -0
  19. package/dist/models/FieldPolicy.d.ts.map +1 -0
  20. package/dist/permissions.d.ts +9 -0
  21. package/dist/permissions.d.ts.map +1 -0
  22. package/dist/playground.d.ts +2 -0
  23. package/dist/playground.d.ts.map +1 -0
  24. package/dist/playground.js +39 -0
  25. package/dist/playground.js.map +1 -0
  26. package/dist/settings-catalog.d.ts +73 -0
  27. package/dist/settings-catalog.d.ts.map +1 -0
  28. package/dist/smrt-knowledge.json +50 -7
  29. package/dist/svelte/__tests__/FieldPolicyControlPanel.test.js +223 -0
  30. package/dist/svelte/__tests__/FieldPolicyGear.test.js +503 -0
  31. package/dist/svelte/__tests__/FormHelp.test.js +138 -0
  32. package/dist/svelte/__tests__/ModeUX.test.js +109 -0
  33. package/dist/svelte/__tests__/ObjectForm.test.js +556 -0
  34. package/dist/svelte/__tests__/ObjectFormGeneratedApi.integration.test.js +436 -0
  35. package/dist/svelte/__tests__/PolicyField.test.js +373 -0
  36. package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte +30 -0
  37. package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte.d.ts +9 -0
  38. package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte.d.ts.map +1 -0
  39. package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte +33 -0
  40. package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte.d.ts +10 -0
  41. package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte.d.ts.map +1 -0
  42. package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte +19 -0
  43. package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte.d.ts +10 -0
  44. package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte.d.ts.map +1 -0
  45. package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte +18 -0
  46. package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte.d.ts +11 -0
  47. package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte.d.ts.map +1 -0
  48. package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte +11 -0
  49. package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte.d.ts +19 -0
  50. package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte.d.ts.map +1 -0
  51. package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte +18 -0
  52. package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte.d.ts +8 -0
  53. package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte.d.ts.map +1 -0
  54. package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte +24 -0
  55. package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte.d.ts +13 -0
  56. package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte.d.ts.map +1 -0
  57. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte +20 -0
  58. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts +11 -0
  59. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts.map +1 -0
  60. package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte +17 -0
  61. package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte.d.ts +10 -0
  62. package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte.d.ts.map +1 -0
  63. package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte +25 -0
  64. package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte.d.ts +10 -0
  65. package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte.d.ts.map +1 -0
  66. package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte +34 -0
  67. package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte.d.ts +10 -0
  68. package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte.d.ts.map +1 -0
  69. package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte +29 -0
  70. package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte.d.ts +11 -0
  71. package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte.d.ts.map +1 -0
  72. package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte +33 -0
  73. package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte.d.ts +9 -0
  74. package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte.d.ts.map +1 -0
  75. package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte +40 -0
  76. package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte.d.ts +8 -0
  77. package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte.d.ts.map +1 -0
  78. package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte +26 -0
  79. package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte.d.ts +13 -0
  80. package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte.d.ts.map +1 -0
  81. package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte +39 -0
  82. package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte.d.ts +17 -0
  83. package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte.d.ts.map +1 -0
  84. package/dist/svelte/__tests__/fixtures/RegistryInput.svelte +15 -0
  85. package/dist/svelte/__tests__/fixtures/RegistryInput.svelte.d.ts +5 -0
  86. package/dist/svelte/__tests__/fixtures/RegistryInput.svelte.d.ts.map +1 -0
  87. package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte +29 -0
  88. package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte.d.ts +14 -0
  89. package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte.d.ts.map +1 -0
  90. package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte +24 -0
  91. package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte.d.ts +14 -0
  92. package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte.d.ts.map +1 -0
  93. package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte +28 -0
  94. package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte.d.ts +13 -0
  95. package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte.d.ts.map +1 -0
  96. package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte +14 -0
  97. package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte.d.ts +19 -0
  98. package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte.d.ts.map +1 -0
  99. package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte +25 -0
  100. package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte.d.ts +14 -0
  101. package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte.d.ts.map +1 -0
  102. package/dist/svelte/__tests__/settings-catalog.test.js +161 -0
  103. package/dist/svelte/components/AdvancedFields.svelte +49 -0
  104. package/dist/svelte/components/AdvancedFields.svelte.d.ts +17 -0
  105. package/dist/svelte/components/AdvancedFields.svelte.d.ts.map +1 -0
  106. package/dist/svelte/components/FieldInput.svelte +159 -0
  107. package/dist/svelte/components/FieldInput.svelte.d.ts +5 -0
  108. package/dist/svelte/components/FieldInput.svelte.d.ts.map +1 -0
  109. package/dist/svelte/components/FieldPolicyControlPanel.svelte +261 -0
  110. package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts +26 -0
  111. package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts.map +1 -0
  112. package/dist/svelte/components/FieldPolicyEditor.svelte +417 -0
  113. package/dist/svelte/components/FieldPolicyEditor.svelte.d.ts +18 -0
  114. package/dist/svelte/components/FieldPolicyEditor.svelte.d.ts.map +1 -0
  115. package/dist/svelte/components/FieldPolicyGearButton.svelte +17 -0
  116. package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts +8 -0
  117. package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts.map +1 -0
  118. package/dist/svelte/components/FieldPolicyGearProvider.svelte +135 -0
  119. package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts +17 -0
  120. package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts.map +1 -0
  121. package/dist/svelte/components/FieldPolicyProvider.svelte +89 -0
  122. package/dist/svelte/components/FieldPolicyProvider.svelte.d.ts +35 -0
  123. package/dist/svelte/components/FieldPolicyProvider.svelte.d.ts.map +1 -0
  124. package/dist/svelte/components/FormHelp.svelte +157 -0
  125. package/dist/svelte/components/FormHelp.svelte.d.ts +15 -0
  126. package/dist/svelte/components/FormHelp.svelte.d.ts.map +1 -0
  127. package/dist/svelte/components/ModeSwitch.svelte +47 -0
  128. package/dist/svelte/components/ModeSwitch.svelte.d.ts +10 -0
  129. package/dist/svelte/components/ModeSwitch.svelte.d.ts.map +1 -0
  130. package/dist/svelte/components/ObjectForm.svelte +351 -0
  131. package/dist/svelte/components/ObjectForm.svelte.d.ts +40 -0
  132. package/dist/svelte/components/ObjectForm.svelte.d.ts.map +1 -0
  133. package/dist/svelte/components/ObjectFormSourceProvider.svelte +18 -0
  134. package/dist/svelte/components/ObjectFormSourceProvider.svelte.d.ts +11 -0
  135. package/dist/svelte/components/ObjectFormSourceProvider.svelte.d.ts.map +1 -0
  136. package/dist/svelte/components/PolicyField.svelte +338 -0
  137. package/dist/svelte/components/PolicyField.svelte.d.ts +80 -0
  138. package/dist/svelte/components/PolicyField.svelte.d.ts.map +1 -0
  139. package/dist/svelte/context.svelte.d.ts +54 -0
  140. package/dist/svelte/context.svelte.d.ts.map +1 -0
  141. package/dist/svelte/context.svelte.js +55 -0
  142. package/dist/svelte/data-table.d.ts +17 -0
  143. package/dist/svelte/data-table.d.ts.map +1 -0
  144. package/dist/svelte/data-table.js +19 -0
  145. package/dist/svelte/field-policy-editor.d.ts +81 -0
  146. package/dist/svelte/field-policy-editor.d.ts.map +1 -0
  147. package/dist/svelte/field-policy-editor.js +120 -0
  148. package/dist/svelte/gear-context.svelte.d.ts +14 -0
  149. package/dist/svelte/gear-context.svelte.d.ts.map +1 -0
  150. package/dist/svelte/gear-context.svelte.js +15 -0
  151. package/dist/svelte/index.d.ts +58 -0
  152. package/dist/svelte/index.d.ts.map +1 -0
  153. package/dist/svelte/index.js +38 -0
  154. package/dist/svelte/input-registry.d.ts +21 -0
  155. package/dist/svelte/input-registry.d.ts.map +1 -0
  156. package/dist/svelte/input-registry.js +26 -0
  157. package/dist/svelte/object-form-source-context.svelte.d.ts +4 -0
  158. package/dist/svelte/object-form-source-context.svelte.d.ts.map +1 -0
  159. package/dist/svelte/object-form-source-context.svelte.js +8 -0
  160. package/dist/svelte/object-form-source.svelte.d.ts +18 -0
  161. package/dist/svelte/object-form-source.svelte.d.ts.map +1 -0
  162. package/dist/svelte/object-form-source.svelte.js +99 -0
  163. package/dist/svelte/object-form.d.ts +6 -0
  164. package/dist/svelte/object-form.d.ts.map +1 -0
  165. package/dist/svelte/object-form.js +38 -0
  166. package/dist/svelte/playground/FieldPolicyFormPreview.svelte +212 -0
  167. package/dist/svelte/playground/FieldPolicyFormPreview.svelte.d.ts +4 -0
  168. package/dist/svelte/playground/FieldPolicyFormPreview.svelte.d.ts.map +1 -0
  169. package/dist/svelte/playground/ObjectFormPreview.svelte +129 -0
  170. package/dist/svelte/playground/ObjectFormPreview.svelte.d.ts +4 -0
  171. package/dist/svelte/playground/ObjectFormPreview.svelte.d.ts.map +1 -0
  172. package/dist/svelte/playground.d.ts +20 -0
  173. package/dist/svelte/playground.d.ts.map +1 -0
  174. package/dist/svelte/playground.js +38 -0
  175. package/dist/svelte/settings-catalog.d.ts +54 -0
  176. package/dist/svelte/settings-catalog.d.ts.map +1 -0
  177. package/dist/svelte/settings-catalog.js +116 -0
  178. package/dist/svelte/types.d.ts +101 -0
  179. package/dist/svelte/types.d.ts.map +1 -0
  180. package/dist/svelte/types.js +4 -0
  181. package/dist/types.d.ts +276 -203
  182. package/dist/types.d.ts.map +1 -0
  183. package/dist/types.js.map +1 -1
  184. package/package.json +33 -8
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Test fixture: PolicyField with helpDensity='tooltip' to verify the help
4
+ * text attaches to the label as a title attribute instead of rendering a
5
+ * visible hint.
6
+ */
7
+
8
+ import type { ResolvedObjectFieldPolicy } from '../../../types.js';
9
+ import FieldPolicyProvider from '../../components/FieldPolicyProvider.svelte';
10
+ import PolicyField from '../../components/PolicyField.svelte';
11
+
12
+ let {
13
+ policy,
14
+ helpDensity = 'tooltip',
15
+ }: {
16
+ policy: ResolvedObjectFieldPolicy;
17
+ helpDensity?: 'hint' | 'tooltip';
18
+ } = $props();
19
+ </script>
20
+
21
+ <FieldPolicyProvider {policy} mode="basic">
22
+ <PolicyField name="name" {helpDensity}>
23
+ <input id="name" name="name" type="text" />
24
+ </PolicyField>
25
+ </FieldPolicyProvider>
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Test fixture: PolicyField with helpDensity='tooltip' to verify the help
3
+ * text attaches to the label as a title attribute instead of rendering a
4
+ * visible hint.
5
+ */
6
+ import type { ResolvedObjectFieldPolicy } from '../../../types.js';
7
+ type $$ComponentProps = {
8
+ policy: ResolvedObjectFieldPolicy;
9
+ helpDensity?: 'hint' | 'tooltip';
10
+ };
11
+ declare const TooltipFixture: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type TooltipFixture = ReturnType<typeof TooltipFixture>;
13
+ export default TooltipFixture;
14
+ //# sourceMappingURL=TooltipFixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TooltipFixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/__tests__/fixtures/TooltipFixture.svelte.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAIlE,KAAK,gBAAgB,GAAI;IACxB,MAAM,EAAE,yBAAyB,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAsBF,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,161 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { auditObjectRefs, decorateCatalogPage, fieldPolicyCatalogPreservedParams, fieldPolicyControlPanelNavItem, fieldPolicyRollup, orgRowIdsForObject, prunableDriftRows, } from '../settings-catalog.js';
3
+ const objectRef = '@test/smrt-fields:Document';
4
+ function audit() {
5
+ return {
6
+ caller: {
7
+ tenantId: 'tenant',
8
+ userId: 'user',
9
+ canManageOrg: true,
10
+ canPersonalize: true,
11
+ },
12
+ appRows: [],
13
+ orgRows: [
14
+ {
15
+ id: 'org-row',
16
+ objectRef,
17
+ fieldName: 'title',
18
+ scopeType: 'tenant',
19
+ tenantId: 'tenant',
20
+ userId: null,
21
+ updatedBy: 'user',
22
+ createdAt: null,
23
+ updatedAt: null,
24
+ defaultValue: null,
25
+ displayOrder: null,
26
+ help: null,
27
+ label: null,
28
+ locked: null,
29
+ visibility: 'advanced',
30
+ },
31
+ ],
32
+ inheritedOrgKeys: {},
33
+ userOverrideCounts: { [objectRef]: { title: 2 } },
34
+ driftRows: [
35
+ {
36
+ id: 'stale',
37
+ objectRef: '@gone:Old',
38
+ fieldName: 'gone',
39
+ scopeType: 'tenant',
40
+ tenantId: 'tenant',
41
+ userId: null,
42
+ updatedBy: null,
43
+ createdAt: null,
44
+ updatedAt: null,
45
+ reason: 'unknown-object',
46
+ prunable: true,
47
+ },
48
+ ],
49
+ policies: {
50
+ [objectRef]: {
51
+ objectRef,
52
+ fields: {
53
+ title: {
54
+ fieldName: 'title',
55
+ hasDefault: true,
56
+ defaultValue: 'tenant',
57
+ visibility: 'advanced',
58
+ help: null,
59
+ label: null,
60
+ order: null,
61
+ group: null,
62
+ locked: false,
63
+ required: false,
64
+ },
65
+ },
66
+ layers: {
67
+ title: [
68
+ {
69
+ layer: 'code',
70
+ delta: { default: { value: 'code' }, visibility: 'basic' },
71
+ },
72
+ {
73
+ layer: 'tenant',
74
+ tenantId: 'tenant',
75
+ delta: { default: { value: 'tenant' }, visibility: 'advanced' },
76
+ },
77
+ ],
78
+ },
79
+ },
80
+ },
81
+ };
82
+ }
83
+ describe('field-policy control-panel adapters', () => {
84
+ it('replays explained layers for code/app/org cells and never exposes user rows', () => {
85
+ const rollup = fieldPolicyRollup(audit(), objectRef, 'title');
86
+ expect(rollup.code).toMatchObject({
87
+ defaultValue: 'code',
88
+ visibility: 'basic',
89
+ });
90
+ expect(rollup.org).toMatchObject({
91
+ defaultValue: 'tenant',
92
+ visibility: 'advanced',
93
+ contributed: true,
94
+ });
95
+ expect(rollup.userCount).toBe(2);
96
+ expect(rollup.orgRow?.id).toBe('org-row');
97
+ });
98
+ it('keeps a selected off-page object in the capped policy read while count refs remain complete', () => {
99
+ const page = {
100
+ items: Array.from({ length: 100 }, (_, index) => ({
101
+ objectRef: `@test:Thing${index}`,
102
+ })),
103
+ selected: { objectRef: '@test:Selected' },
104
+ };
105
+ const refs = auditObjectRefs(page);
106
+ expect(refs.objectRefs).toHaveLength(100);
107
+ expect(refs.objectRefs[0]).toBe('@test:Selected');
108
+ expect(refs.countObjectRefs).toHaveLength(101);
109
+ });
110
+ it('decorates an off-page selected detail alongside page summaries', () => {
111
+ const page = decorateCatalogPage({
112
+ items: [],
113
+ selected: {
114
+ id: 'selected',
115
+ objectRef,
116
+ fieldName: 'title',
117
+ className: 'Document',
118
+ packageName: '@test/smrt-fields',
119
+ label: 'Raw title',
120
+ description: 'Raw description',
121
+ fields: {},
122
+ },
123
+ query: '',
124
+ page: 1,
125
+ pageSize: 25,
126
+ total: 1,
127
+ }, audit());
128
+ expect(page.selected).toMatchObject({
129
+ label: 'Title',
130
+ status: 'Organization · 2 user overrides',
131
+ fields: {},
132
+ });
133
+ });
134
+ it('selects only caller-prunable drift rows and produces a permission-gated tenant nav item', () => {
135
+ expect(prunableDriftRows(audit()).map((row) => row.id)).toEqual(['stale']);
136
+ expect(orgRowIdsForObject(audit(), objectRef)).toEqual(['org-row']);
137
+ expect(fieldPolicyControlPanelNavItem({
138
+ href: '/admin/fields',
139
+ permissions: [],
140
+ })).toBeNull();
141
+ expect(fieldPolicyControlPanelNavItem({
142
+ href: '/admin/fields',
143
+ permissions: ['fields.policy.manage'],
144
+ })).toMatchObject({ href: '/admin/fields', label: 'Field settings' });
145
+ });
146
+ it('keeps a nondefault page size across catalog search, selection, and pagination links', () => {
147
+ expect(fieldPolicyCatalogPreservedParams({
148
+ page: { pageSize: 25 },
149
+ filters: {
150
+ packageFilter: 'package',
151
+ objectFilter: 'object',
152
+ customizedOnly: true,
153
+ },
154
+ })).toEqual({
155
+ package: 'package',
156
+ object: 'object',
157
+ customized: '1',
158
+ pageSize: '25',
159
+ });
160
+ });
161
+ });
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ /**
3
+ * AdvancedFields — a disclosure section for advanced-tier fields.
4
+ *
5
+ * An optional composition that wraps PolicyField-wrapped advanced fields. Uses
6
+ * `@happyvertical/smrt-ui/ui`'s Disclosure (native `<details>/<summary>` —
7
+ * good a11y baseline). The title shows the advanced field count from the
8
+ * resolved policy.
9
+ *
10
+ * The disclosure's open state is **not** coupled to the provider's mode: the
11
+ * mode determines *visibility* (advanced fields are hidden in basic mode),
12
+ * while the disclosure provides a *collapsed* view of those same fields when
13
+ * they are visible (advanced mode). Consumers who prefer the mode toggle alone
14
+ * (ModeSwitch) do not need this component.
15
+ *
16
+ * Must be used inside a FieldPolicyProvider.
17
+ */
18
+ import { Disclosure } from '@happyvertical/smrt-ui/ui';
19
+ import type { Snippet } from 'svelte';
20
+ import { getFieldPolicyContext } from '../context.svelte.js';
21
+
22
+ export interface Props {
23
+ /**
24
+ * Title for the disclosure. Defaults to "Advanced" with the field count.
25
+ */
26
+ title?: string;
27
+ /** Whether the disclosure starts open. */
28
+ open?: boolean;
29
+ /** Children — the advanced fields to render inside the disclosure. */
30
+ children?: Snippet;
31
+ /** Additional class. */
32
+ class?: string;
33
+ }
34
+
35
+ let { title, open = false, children, class: className = '' }: Props = $props();
36
+
37
+ const context = getFieldPolicyContext();
38
+
39
+ const resolvedTitle = $derived(
40
+ title ??
41
+ `Advanced · ${context.advancedFieldCount} field${context.advancedFieldCount === 1 ? '' : 's'}`,
42
+ );
43
+ </script>
44
+
45
+ {#if context.mode.current === 'advanced' && context.advancedFieldCount > 0}
46
+ <Disclosure title={resolvedTitle} {open} class={className}>
47
+ {@render children?.()}
48
+ </Disclosure>
49
+ {/if}
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ export interface Props {
3
+ /**
4
+ * Title for the disclosure. Defaults to "Advanced" with the field count.
5
+ */
6
+ title?: string;
7
+ /** Whether the disclosure starts open. */
8
+ open?: boolean;
9
+ /** Children — the advanced fields to render inside the disclosure. */
10
+ children?: Snippet;
11
+ /** Additional class. */
12
+ class?: string;
13
+ }
14
+ declare const AdvancedFields: import("svelte").Component<Props, {}, "">;
15
+ type AdvancedFields = ReturnType<typeof AdvancedFields>;
16
+ export default AdvancedFields;
17
+ //# sourceMappingURL=AdvancedFields.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdvancedFields.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AdvancedFields.svelte.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAItC,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sEAAsE;IACtE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0BD,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,159 @@
1
+ <script lang="ts">
2
+ /** Provider-free built-in adapters for the SMRT generated wire field types. */
3
+ import { Input, Textarea, Toggle } from '@happyvertical/smrt-ui/forms';
4
+ import { onDestroy } from 'svelte';
5
+ import type { FieldInputProps } from '../types.js';
6
+
7
+ let {
8
+ id,
9
+ name,
10
+ label,
11
+ field,
12
+ value,
13
+ required,
14
+ disabled,
15
+ onvaluechange,
16
+ onvaliditychange,
17
+ }: FieldInputProps = $props();
18
+
19
+ let jsonText = $state('');
20
+ let jsonError = $state<string | null>(null);
21
+
22
+ onDestroy(() => {
23
+ // A hidden/unmounted invalid JSON control must not leave its parent form
24
+ // permanently blocked after a mode change.
25
+ if (jsonError) onvaliditychange?.(true);
26
+ });
27
+
28
+ function formatJson(next: unknown): string {
29
+ if (next === undefined || next === null) return '';
30
+ return JSON.stringify(next, null, 2);
31
+ }
32
+
33
+ $effect(() => {
34
+ // Keep external record updates visible, but never overwrite an invalid JSON
35
+ // draft while the user is correcting it. Compare serialized text rather than
36
+ // object identity: callers may pass a fresh record object each render.
37
+ if (jsonError) return;
38
+ const next = formatJson(value);
39
+ if (jsonText !== next) jsonText = next;
40
+ });
41
+
42
+ function setText(event: Event): void {
43
+ onvaluechange((event.currentTarget as HTMLInputElement).value);
44
+ }
45
+
46
+ function setNumber(event: Event, integer: boolean): void {
47
+ const input = event.currentTarget as HTMLInputElement;
48
+ // Browsers report incomplete number text (for example a lone sign or an
49
+ // unfinished exponent) as `value === ''` plus `badInput`. That is not a
50
+ // deliberate clear, so leave the record untouched until the edit is valid.
51
+ if (input.validity.badInput) return;
52
+ const raw = input.value;
53
+ if (raw === '') {
54
+ onvaluechange(undefined);
55
+ return;
56
+ }
57
+ const number = Number(raw);
58
+ // Number inputs can transiently contain incomplete exponent/sign text while
59
+ // editing. Keep the last valid record value until the browser supplies a
60
+ // finite number; never leak NaN/Infinity into a create or edit payload.
61
+ if (!Number.isFinite(number)) return;
62
+ onvaluechange(integer ? Math.trunc(number) : number);
63
+ }
64
+
65
+ function setJson(event: Event): void {
66
+ jsonText = (event.currentTarget as HTMLTextAreaElement).value;
67
+ if (jsonText.trim() === '') {
68
+ jsonError = null;
69
+ onvaliditychange?.(true);
70
+ onvaluechange(undefined);
71
+ return;
72
+ }
73
+ try {
74
+ const parsed = JSON.parse(jsonText);
75
+ jsonError = null;
76
+ onvaliditychange?.(true);
77
+ onvaluechange(parsed);
78
+ } catch {
79
+ jsonError = 'Enter valid JSON before submitting this form.';
80
+ onvaliditychange?.(false);
81
+ }
82
+ }
83
+
84
+ function formatDateTimeLocal(value: unknown): string {
85
+ const date = value instanceof Date ? value : new Date(String(value));
86
+ if (Number.isNaN(date.getTime())) return '';
87
+ const part = (number: number) => String(number).padStart(2, '0');
88
+ return `${date.getFullYear()}-${part(date.getMonth() + 1)}-${part(date.getDate())}T${part(date.getHours())}:${part(date.getMinutes())}`;
89
+ }
90
+
91
+ function setDateTime(event: Event): void {
92
+ const raw = (event.currentTarget as HTMLInputElement).value;
93
+ if (!raw) {
94
+ onvaluechange(undefined);
95
+ return;
96
+ }
97
+ const date = new Date(raw);
98
+ onvaluechange(Number.isNaN(date.getTime()) ? raw : date.toISOString());
99
+ }
100
+ </script>
101
+
102
+ {#if field.definition.type === 'boolean'}
103
+ <Toggle
104
+ {id}
105
+ {name}
106
+ checked={value === true}
107
+ {disabled}
108
+ ariaLabel={label ?? name}
109
+ onchange={onvaluechange}
110
+ />
111
+ {:else if field.definition.type === 'json'}
112
+ <Textarea
113
+ {id}
114
+ {name}
115
+ value={jsonText}
116
+ {disabled}
117
+ {required}
118
+ rows={8}
119
+ aria-invalid={jsonError ? 'true' : undefined}
120
+ aria-describedby={jsonError ? `${id}-json-error` : undefined}
121
+ oninput={setJson}
122
+ />
123
+ {#if jsonError}
124
+ <p id={`${id}-json-error`} class="field-input__error" role="alert">{jsonError}</p>
125
+ {/if}
126
+ {:else if field.definition.type === 'integer' || field.definition.type === 'decimal'}
127
+ <Input
128
+ {id}
129
+ {name}
130
+ type="number"
131
+ step={field.definition.type === 'integer' ? '1' : 'any'}
132
+ value={typeof value === 'number' ? value : ''}
133
+ {disabled}
134
+ {required}
135
+ oninput={(event: Event) => setNumber(event, field.definition.type === 'integer')}
136
+ />
137
+ {:else if field.definition.type === 'datetime'}
138
+ <Input
139
+ {id}
140
+ {name}
141
+ type="datetime-local"
142
+ value={formatDateTimeLocal(value)}
143
+ {disabled}
144
+ {required}
145
+ oninput={setDateTime}
146
+ />
147
+ {:else}
148
+ <Input
149
+ {id}
150
+ {name}
151
+ type="text"
152
+ value={typeof value === 'string' ? value : ''}
153
+ {disabled}
154
+ {required}
155
+ oninput={setText}
156
+ />
157
+ {/if}
158
+
159
+ <style>.field-input__error { color: var(--smrt-color-error, #b42318); margin: 0; }</style>
@@ -0,0 +1,5 @@
1
+ import type { FieldInputProps } from '../types.js';
2
+ declare const FieldInput: import("svelte").Component<FieldInputProps, {}, "">;
3
+ type FieldInput = ReturnType<typeof FieldInput>;
4
+ export default FieldInput;
5
+ //# sourceMappingURL=FieldInput.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/FieldInput.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA0HnD,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}