@happyvertical/smrt-fields 0.40.59 → 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.
- package/AGENTS.md +58 -12
- package/README.md +90 -2
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/cache.d.ts +18 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/collections/FieldPolicyCollection.d.ts +95 -0
- package/dist/collections/FieldPolicyCollection.d.ts.map +1 -0
- package/dist/field-definitions.d.ts +77 -0
- package/dist/field-definitions.d.ts.map +1 -0
- package/dist/field-policy-resolver.d.ts +46 -0
- package/dist/field-policy-resolver.d.ts.map +1 -0
- package/dist/index.d.ts +9 -532
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +752 -55
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +45 -3
- package/dist/models/FieldPolicy.d.ts +163 -0
- package/dist/models/FieldPolicy.d.ts.map +1 -0
- package/dist/permissions.d.ts +9 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +39 -0
- package/dist/playground.js.map +1 -0
- package/dist/settings-catalog.d.ts +73 -0
- package/dist/settings-catalog.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +50 -7
- package/dist/svelte/__tests__/FieldPolicyControlPanel.test.js +223 -0
- package/dist/svelte/__tests__/FieldPolicyGear.test.js +503 -0
- package/dist/svelte/__tests__/FormHelp.test.js +138 -0
- package/dist/svelte/__tests__/ModeUX.test.js +109 -0
- package/dist/svelte/__tests__/ObjectForm.test.js +556 -0
- package/dist/svelte/__tests__/ObjectFormGeneratedApi.integration.test.js +436 -0
- package/dist/svelte/__tests__/PolicyField.test.js +373 -0
- package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte +30 -0
- package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte.d.ts +9 -0
- package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte +33 -0
- package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte.d.ts +10 -0
- package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte +19 -0
- package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte.d.ts +10 -0
- package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte +18 -0
- package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte.d.ts +11 -0
- package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte +11 -0
- package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte.d.ts +19 -0
- package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte +18 -0
- package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte.d.ts +8 -0
- package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte +24 -0
- package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte.d.ts +13 -0
- package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte +20 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts +11 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte +17 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte.d.ts +10 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte +25 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte.d.ts +10 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte +34 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte.d.ts +10 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte +29 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte.d.ts +11 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte +33 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte.d.ts +9 -0
- package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte +40 -0
- package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte.d.ts +8 -0
- package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte +26 -0
- package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte.d.ts +13 -0
- package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte +39 -0
- package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte.d.ts +17 -0
- package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/RegistryInput.svelte +15 -0
- package/dist/svelte/__tests__/fixtures/RegistryInput.svelte.d.ts +5 -0
- package/dist/svelte/__tests__/fixtures/RegistryInput.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte +29 -0
- package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte.d.ts +14 -0
- package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte +24 -0
- package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte.d.ts +14 -0
- package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte +28 -0
- package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte.d.ts +13 -0
- package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte +14 -0
- package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte.d.ts +19 -0
- package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte +25 -0
- package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte.d.ts +14 -0
- package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte.d.ts.map +1 -0
- package/dist/svelte/__tests__/settings-catalog.test.js +161 -0
- package/dist/svelte/components/AdvancedFields.svelte +49 -0
- package/dist/svelte/components/AdvancedFields.svelte.d.ts +17 -0
- package/dist/svelte/components/AdvancedFields.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FieldInput.svelte +159 -0
- package/dist/svelte/components/FieldInput.svelte.d.ts +5 -0
- package/dist/svelte/components/FieldInput.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FieldPolicyControlPanel.svelte +261 -0
- package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts +26 -0
- package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FieldPolicyEditor.svelte +417 -0
- package/dist/svelte/components/FieldPolicyEditor.svelte.d.ts +18 -0
- package/dist/svelte/components/FieldPolicyEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FieldPolicyGearButton.svelte +17 -0
- package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts +8 -0
- package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FieldPolicyGearProvider.svelte +135 -0
- package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts +17 -0
- package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FieldPolicyProvider.svelte +89 -0
- package/dist/svelte/components/FieldPolicyProvider.svelte.d.ts +35 -0
- package/dist/svelte/components/FieldPolicyProvider.svelte.d.ts.map +1 -0
- package/dist/svelte/components/FormHelp.svelte +157 -0
- package/dist/svelte/components/FormHelp.svelte.d.ts +15 -0
- package/dist/svelte/components/FormHelp.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ModeSwitch.svelte +47 -0
- package/dist/svelte/components/ModeSwitch.svelte.d.ts +10 -0
- package/dist/svelte/components/ModeSwitch.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ObjectForm.svelte +351 -0
- package/dist/svelte/components/ObjectForm.svelte.d.ts +40 -0
- package/dist/svelte/components/ObjectForm.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ObjectFormSourceProvider.svelte +18 -0
- package/dist/svelte/components/ObjectFormSourceProvider.svelte.d.ts +11 -0
- package/dist/svelte/components/ObjectFormSourceProvider.svelte.d.ts.map +1 -0
- package/dist/svelte/components/PolicyField.svelte +338 -0
- package/dist/svelte/components/PolicyField.svelte.d.ts +80 -0
- package/dist/svelte/components/PolicyField.svelte.d.ts.map +1 -0
- package/dist/svelte/context.svelte.d.ts +54 -0
- package/dist/svelte/context.svelte.d.ts.map +1 -0
- package/dist/svelte/context.svelte.js +55 -0
- package/dist/svelte/data-table.d.ts +17 -0
- package/dist/svelte/data-table.d.ts.map +1 -0
- package/dist/svelte/data-table.js +19 -0
- package/dist/svelte/field-policy-editor.d.ts +81 -0
- package/dist/svelte/field-policy-editor.d.ts.map +1 -0
- package/dist/svelte/field-policy-editor.js +120 -0
- package/dist/svelte/gear-context.svelte.d.ts +14 -0
- package/dist/svelte/gear-context.svelte.d.ts.map +1 -0
- package/dist/svelte/gear-context.svelte.js +15 -0
- package/dist/svelte/index.d.ts +58 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +38 -0
- package/dist/svelte/input-registry.d.ts +21 -0
- package/dist/svelte/input-registry.d.ts.map +1 -0
- package/dist/svelte/input-registry.js +26 -0
- package/dist/svelte/object-form-source-context.svelte.d.ts +4 -0
- package/dist/svelte/object-form-source-context.svelte.d.ts.map +1 -0
- package/dist/svelte/object-form-source-context.svelte.js +8 -0
- package/dist/svelte/object-form-source.svelte.d.ts +18 -0
- package/dist/svelte/object-form-source.svelte.d.ts.map +1 -0
- package/dist/svelte/object-form-source.svelte.js +99 -0
- package/dist/svelte/object-form.d.ts +6 -0
- package/dist/svelte/object-form.d.ts.map +1 -0
- package/dist/svelte/object-form.js +38 -0
- package/dist/svelte/playground/FieldPolicyFormPreview.svelte +212 -0
- package/dist/svelte/playground/FieldPolicyFormPreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/FieldPolicyFormPreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground/ObjectFormPreview.svelte +129 -0
- package/dist/svelte/playground/ObjectFormPreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/ObjectFormPreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground.d.ts +20 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +38 -0
- package/dist/svelte/settings-catalog.d.ts +54 -0
- package/dist/svelte/settings-catalog.d.ts.map +1 -0
- package/dist/svelte/settings-catalog.js +116 -0
- package/dist/svelte/types.d.ts +101 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +4 -0
- package/dist/types.d.ts +276 -203
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +33 -8
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Playground preview (#2048 AC1): a hand-written widget form that adopts
|
|
4
|
+
* PolicyField on a subset of its fields WITHOUT giving up its markup.
|
|
5
|
+
*
|
|
6
|
+
* The resolved policy is passed as a prop (the headless contract — the app
|
|
7
|
+
* supplies the policy, no smrt-web import). Two fields adopt PolicyField
|
|
8
|
+
* (name, sku) to gain help hints + required markers + prefill; the remaining
|
|
9
|
+
* fields stay as raw hand-written markup, proving "unwrapped fields keep
|
|
10
|
+
* working untouched" and "no layout change".
|
|
11
|
+
*
|
|
12
|
+
* The Basic/Advanced switch + disclosure + form help demonstrate the mode UX
|
|
13
|
+
* and glossary affordances layered over the same hand-written form.
|
|
14
|
+
*/
|
|
15
|
+
import { Input, Textarea } from '@happyvertical/smrt-ui/forms';
|
|
16
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
17
|
+
import type { ResolvedObjectFieldPolicy } from '../../types.js';
|
|
18
|
+
import {
|
|
19
|
+
AdvancedFields,
|
|
20
|
+
FieldPolicyProvider,
|
|
21
|
+
FormHelp,
|
|
22
|
+
ModeSwitch,
|
|
23
|
+
PolicyField,
|
|
24
|
+
} from '../index.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A resolved policy shaped exactly like `resolveFieldPolicy()` / the
|
|
28
|
+
* `resolveBatch` endpoint response. In a real app this comes from the server
|
|
29
|
+
* or the batch endpoint; here it is a static fixture for the mock playground.
|
|
30
|
+
*/
|
|
31
|
+
const resolvedPolicy: ResolvedObjectFieldPolicy = {
|
|
32
|
+
objectRef: '@happyvertical/smrt-products:Product',
|
|
33
|
+
fields: {
|
|
34
|
+
name: {
|
|
35
|
+
fieldName: 'name',
|
|
36
|
+
hasDefault: false,
|
|
37
|
+
defaultValue: undefined,
|
|
38
|
+
visibility: 'basic',
|
|
39
|
+
help: 'The customer-facing product name. Keep it short and unique.',
|
|
40
|
+
label: 'Product name',
|
|
41
|
+
order: 1,
|
|
42
|
+
group: null,
|
|
43
|
+
locked: false,
|
|
44
|
+
required: true,
|
|
45
|
+
},
|
|
46
|
+
sku: {
|
|
47
|
+
fieldName: 'sku',
|
|
48
|
+
hasDefault: true,
|
|
49
|
+
defaultValue: 'SKU-0000',
|
|
50
|
+
visibility: 'basic',
|
|
51
|
+
help: 'Stock-keeping unit. Auto-prefilled from the code default.',
|
|
52
|
+
label: 'SKU',
|
|
53
|
+
order: 2,
|
|
54
|
+
group: null,
|
|
55
|
+
locked: false,
|
|
56
|
+
required: false,
|
|
57
|
+
},
|
|
58
|
+
wholesalePrice: {
|
|
59
|
+
fieldName: 'wholesalePrice',
|
|
60
|
+
hasDefault: true,
|
|
61
|
+
defaultValue: 0,
|
|
62
|
+
visibility: 'advanced',
|
|
63
|
+
help: 'Per-unit price for wholesale channels. Hidden in basic mode.',
|
|
64
|
+
label: 'Wholesale price',
|
|
65
|
+
order: 3,
|
|
66
|
+
group: 'pricing',
|
|
67
|
+
locked: false,
|
|
68
|
+
required: false,
|
|
69
|
+
},
|
|
70
|
+
reorderPoint: {
|
|
71
|
+
fieldName: 'reorderPoint',
|
|
72
|
+
hasDefault: false,
|
|
73
|
+
defaultValue: undefined,
|
|
74
|
+
visibility: 'advanced',
|
|
75
|
+
help: 'Inventory level that triggers a reorder suggestion.',
|
|
76
|
+
label: 'Reorder point',
|
|
77
|
+
order: 4,
|
|
78
|
+
group: 'inventory',
|
|
79
|
+
locked: false,
|
|
80
|
+
required: false,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
let name = $state('');
|
|
86
|
+
let sku = $state('');
|
|
87
|
+
let wholesalePrice = $state('');
|
|
88
|
+
let reorderPoint = $state('');
|
|
89
|
+
// A raw, unwrapped field — deliberately NOT wrapped in PolicyField.
|
|
90
|
+
let rawNotes = $state('');
|
|
91
|
+
|
|
92
|
+
let submitted = $state<string | null>(null);
|
|
93
|
+
|
|
94
|
+
function handleSubmit() {
|
|
95
|
+
submitted = `Saved "${name || '(unnamed)'}" (sku: ${sku || '(auto)'})`;
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<section class="field-policy-preview">
|
|
100
|
+
<header class="preview-header">
|
|
101
|
+
<h2>Hand-written form adopting PolicyField</h2>
|
|
102
|
+
<p class="preview-desc">
|
|
103
|
+
A developer-authored product form. Two fields (name, SKU) adopt
|
|
104
|
+
PolicyField for help, defaults, and progressive disclosure; the rest stay
|
|
105
|
+
hand-written. Toggle the mode switch to reveal advanced-tier fields.
|
|
106
|
+
</p>
|
|
107
|
+
<FormHelp
|
|
108
|
+
objectDescription="A sellable catalog product with wholesale pricing and inventory controls."
|
|
109
|
+
/>
|
|
110
|
+
</header>
|
|
111
|
+
|
|
112
|
+
<FieldPolicyProvider policy={resolvedPolicy} mode="basic">
|
|
113
|
+
<div class="toolbar">
|
|
114
|
+
<ModeSwitch />
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<form class="widget-form" onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
|
|
118
|
+
<!-- Adopted field: gets label, help hint, required marker from the policy -->
|
|
119
|
+
<PolicyField name="name">
|
|
120
|
+
<Input id="name" name="name" bind:value={name} placeholder="Widget Deluxe" />
|
|
121
|
+
</PolicyField>
|
|
122
|
+
|
|
123
|
+
<!-- Adopted field: prefilled with the resolved default on new records -->
|
|
124
|
+
<PolicyField name="sku">
|
|
125
|
+
<Input id="sku" name="sku" bind:value={sku} />
|
|
126
|
+
</PolicyField>
|
|
127
|
+
|
|
128
|
+
<!-- Unwrapped hand-written field — keeps working untouched -->
|
|
129
|
+
<div class="raw-field">
|
|
130
|
+
<label for="rawNotes">Notes (unwrapped, hand-written)</label>
|
|
131
|
+
<Textarea
|
|
132
|
+
id="rawNotes"
|
|
133
|
+
name="rawNotes"
|
|
134
|
+
bind:value={rawNotes}
|
|
135
|
+
placeholder="Free-form notes, no policy wrapping"
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<!-- Advanced-tier fields live in the disclosure -->
|
|
140
|
+
<AdvancedFields>
|
|
141
|
+
<PolicyField name="wholesalePrice">
|
|
142
|
+
<Input id="wholesalePrice" name="wholesalePrice" type="number" bind:value={wholesalePrice} />
|
|
143
|
+
</PolicyField>
|
|
144
|
+
<PolicyField name="reorderPoint">
|
|
145
|
+
<Input id="reorderPoint" name="reorderPoint" type="number" bind:value={reorderPoint} />
|
|
146
|
+
</PolicyField>
|
|
147
|
+
</AdvancedFields>
|
|
148
|
+
|
|
149
|
+
<div class="actions">
|
|
150
|
+
<Button type="submit">Save product</Button>
|
|
151
|
+
</div>
|
|
152
|
+
</form>
|
|
153
|
+
|
|
154
|
+
{#if submitted}
|
|
155
|
+
<p class="status" role="status">{submitted}</p>
|
|
156
|
+
{/if}
|
|
157
|
+
</FieldPolicyProvider>
|
|
158
|
+
</section>
|
|
159
|
+
|
|
160
|
+
<style>
|
|
161
|
+
.field-policy-preview {
|
|
162
|
+
display: flex;
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
gap: var(--smrt-spacing-4, 1rem);
|
|
165
|
+
max-width: 40rem;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.preview-header h2 {
|
|
169
|
+
font: var(--smrt-typography-headline-small-font, inherit);
|
|
170
|
+
margin: 0 0 var(--smrt-spacing-1, 0.25rem);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.preview-desc {
|
|
174
|
+
font: var(--smrt-typography-body-medium-font, inherit);
|
|
175
|
+
color: var(--smrt-color-on-surface-variant, currentColor);
|
|
176
|
+
margin: 0 0 var(--smrt-spacing-2, 0.5rem);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.toolbar {
|
|
180
|
+
display: flex;
|
|
181
|
+
justify-content: flex-end;
|
|
182
|
+
margin-bottom: var(--smrt-spacing-3, 0.75rem);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.widget-form {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
gap: var(--smrt-spacing-3, 0.75rem);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.raw-field {
|
|
192
|
+
display: flex;
|
|
193
|
+
flex-direction: column;
|
|
194
|
+
gap: var(--smrt-spacing-1, 0.25rem);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.raw-field label {
|
|
198
|
+
font: var(--smrt-typography-label-large-font, inherit);
|
|
199
|
+
color: var(--smrt-color-on-surface, currentColor);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.actions {
|
|
203
|
+
display: flex;
|
|
204
|
+
gap: var(--smrt-spacing-2, 0.5rem);
|
|
205
|
+
padding-top: var(--smrt-spacing-2, 0.5rem);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.status {
|
|
209
|
+
font: var(--smrt-typography-body-medium-font, inherit);
|
|
210
|
+
color: var(--smrt-color-primary, #6750a4);
|
|
211
|
+
}
|
|
212
|
+
</style>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
declare const FieldPolicyFormPreview: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type FieldPolicyFormPreview = ReturnType<typeof FieldPolicyFormPreview>;
|
|
3
|
+
export default FieldPolicyFormPreview;
|
|
4
|
+
//# sourceMappingURL=FieldPolicyFormPreview.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldPolicyFormPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/FieldPolicyFormPreview.svelte.ts"],"names":[],"mappings":"AAqKA,QAAA,MAAM,sBAAsB,2DAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** Generated-form showcase for ObjectForm, including a custom field snippet. */
|
|
3
|
+
import { Input } from '@happyvertical/smrt-ui/forms';
|
|
4
|
+
import type { ResolvedObjectFieldPolicy } from '../../types.js';
|
|
5
|
+
import {
|
|
6
|
+
ObjectForm,
|
|
7
|
+
ObjectFormSourceProvider,
|
|
8
|
+
ObjectFormSourceRegistry,
|
|
9
|
+
} from '../index.js';
|
|
10
|
+
import type {
|
|
11
|
+
ObjectFormFieldDefinition,
|
|
12
|
+
ObjectFormFieldSnippetProps,
|
|
13
|
+
} from '../types.js';
|
|
14
|
+
|
|
15
|
+
const fields: Record<string, ObjectFormFieldDefinition> = {
|
|
16
|
+
name: { type: 'text', required: true },
|
|
17
|
+
sku: { type: 'text' },
|
|
18
|
+
price: { type: 'decimal', ui: { group: 'Pricing', order: 3 } },
|
|
19
|
+
active: { type: 'boolean', ui: { group: 'Publishing', order: 4 } },
|
|
20
|
+
metadata: { type: 'json', ui: { group: 'Publishing', order: 5 } },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const policy: ResolvedObjectFieldPolicy = {
|
|
24
|
+
objectRef: '@happyvertical/smrt-products:Product',
|
|
25
|
+
fields: {
|
|
26
|
+
name: {
|
|
27
|
+
fieldName: 'name',
|
|
28
|
+
hasDefault: false,
|
|
29
|
+
defaultValue: undefined,
|
|
30
|
+
visibility: 'basic',
|
|
31
|
+
help: 'A customer-facing product name.',
|
|
32
|
+
label: 'Product name',
|
|
33
|
+
order: 1,
|
|
34
|
+
group: null,
|
|
35
|
+
locked: false,
|
|
36
|
+
required: true,
|
|
37
|
+
},
|
|
38
|
+
sku: {
|
|
39
|
+
fieldName: 'sku',
|
|
40
|
+
hasDefault: true,
|
|
41
|
+
defaultValue: 'SKU-0000',
|
|
42
|
+
visibility: 'basic',
|
|
43
|
+
help: 'Uses a custom snippet renderer.',
|
|
44
|
+
label: 'SKU',
|
|
45
|
+
order: 2,
|
|
46
|
+
group: null,
|
|
47
|
+
locked: false,
|
|
48
|
+
required: false,
|
|
49
|
+
},
|
|
50
|
+
price: {
|
|
51
|
+
fieldName: 'price',
|
|
52
|
+
hasDefault: false,
|
|
53
|
+
defaultValue: undefined,
|
|
54
|
+
visibility: 'advanced',
|
|
55
|
+
help: null,
|
|
56
|
+
label: 'Price',
|
|
57
|
+
order: 3,
|
|
58
|
+
group: 'Pricing',
|
|
59
|
+
locked: false,
|
|
60
|
+
required: false,
|
|
61
|
+
},
|
|
62
|
+
active: {
|
|
63
|
+
fieldName: 'active',
|
|
64
|
+
hasDefault: true,
|
|
65
|
+
defaultValue: true,
|
|
66
|
+
visibility: 'advanced',
|
|
67
|
+
help: null,
|
|
68
|
+
label: 'Active',
|
|
69
|
+
order: 4,
|
|
70
|
+
group: 'Publishing',
|
|
71
|
+
locked: false,
|
|
72
|
+
required: false,
|
|
73
|
+
},
|
|
74
|
+
metadata: {
|
|
75
|
+
fieldName: 'metadata',
|
|
76
|
+
hasDefault: false,
|
|
77
|
+
defaultValue: undefined,
|
|
78
|
+
visibility: 'advanced',
|
|
79
|
+
help: 'Structured publishing metadata.',
|
|
80
|
+
label: 'Metadata',
|
|
81
|
+
order: 5,
|
|
82
|
+
group: 'Publishing',
|
|
83
|
+
locked: false,
|
|
84
|
+
required: false,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const source = new ObjectFormSourceRegistry({
|
|
90
|
+
resolveBatch: async () => ({ policies: { [policy.objectRef]: policy } }),
|
|
91
|
+
});
|
|
92
|
+
source.register({ objectRef: policy.objectRef, fields });
|
|
93
|
+
|
|
94
|
+
let record = $state<Record<string, unknown>>({});
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
{#snippet skuRenderer(props: ObjectFormFieldSnippetProps)}
|
|
98
|
+
<Input
|
|
99
|
+
id="sku"
|
|
100
|
+
name="sku"
|
|
101
|
+
value={String(props.value ?? '')}
|
|
102
|
+
disabled={props.disabled}
|
|
103
|
+
aria-label="Custom SKU"
|
|
104
|
+
oninput={(event) => props.setValue((event.currentTarget as HTMLInputElement).value.toUpperCase())}
|
|
105
|
+
/>
|
|
106
|
+
{/snippet}
|
|
107
|
+
|
|
108
|
+
<section class="object-form-preview">
|
|
109
|
+
<h2>Generated ObjectForm</h2>
|
|
110
|
+
<p>
|
|
111
|
+
This single form is driven by browser-safe fields and resolved policy. Basic
|
|
112
|
+
fields render immediately; advanced grouped fields appear after switching mode.
|
|
113
|
+
SKU is a per-field custom snippet.
|
|
114
|
+
</p>
|
|
115
|
+
<ObjectFormSourceProvider {source}>
|
|
116
|
+
<ObjectForm
|
|
117
|
+
objectRef={policy.objectRef}
|
|
118
|
+
bind:value={record}
|
|
119
|
+
renderers={{ sku: skuRenderer }}
|
|
120
|
+
/>
|
|
121
|
+
</ObjectFormSourceProvider>
|
|
122
|
+
<output class="record-preview">{JSON.stringify(record)}</output>
|
|
123
|
+
</section>
|
|
124
|
+
|
|
125
|
+
<style>
|
|
126
|
+
.object-form-preview { display: grid; gap: var(--smrt-spacing-3, 0.75rem); max-width: 40rem; }
|
|
127
|
+
h2, p { margin: 0; }
|
|
128
|
+
.record-preview { font: var(--smrt-typography-body-small-font, monospace); color: var(--smrt-color-on-surface-variant, currentColor); overflow-wrap: anywhere; }
|
|
129
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectFormPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/ObjectFormPreview.svelte.ts"],"names":[],"mappings":"AA8HA,QAAA,MAAM,iBAAiB,2DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
packageName: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
description: string;
|
|
5
|
+
entries: {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
loadComponent: () => Promise<typeof import("./playground/FieldPolicyFormPreview.svelte")>;
|
|
10
|
+
order: number;
|
|
11
|
+
tags: string[];
|
|
12
|
+
modes: {
|
|
13
|
+
mock: {
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}[];
|
|
18
|
+
};
|
|
19
|
+
export default _default;
|
|
20
|
+
//# sourceMappingURL=playground.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/svelte/playground.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAWA,wBA+BE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-policy playground previews (#2048).
|
|
3
|
+
*
|
|
4
|
+
* Registered with the shared playground host via the auto-discovered
|
|
5
|
+
* "packages/STAR/src/svelte/playground.ts" convention (smrt-playground vite
|
|
6
|
+
* plugin).
|
|
7
|
+
*/
|
|
8
|
+
const loadFieldPolicyForm = () => import('./playground/FieldPolicyFormPreview.svelte');
|
|
9
|
+
const loadObjectForm = () => import('./playground/ObjectFormPreview.svelte');
|
|
10
|
+
export default {
|
|
11
|
+
packageName: '@happyvertical/smrt-fields',
|
|
12
|
+
displayName: 'Field Policies',
|
|
13
|
+
description: 'Headless form primitives: FieldPolicyProvider, PolicyField, basic/advanced modes, and manifest-sourced help.',
|
|
14
|
+
entries: [
|
|
15
|
+
{
|
|
16
|
+
id: 'policy-form',
|
|
17
|
+
title: 'Policy-Driven Form',
|
|
18
|
+
description: 'A hand-written product form adopting PolicyField on a subset of fields — visibility by mode, default prefill, help hints, and the form-level glossary.',
|
|
19
|
+
loadComponent: loadFieldPolicyForm,
|
|
20
|
+
order: 1,
|
|
21
|
+
tags: ['forms', 'fields', 'policy', 'defaults'],
|
|
22
|
+
modes: {
|
|
23
|
+
mock: {
|
|
24
|
+
label: 'Mock',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'object-form',
|
|
30
|
+
title: 'Generated ObjectForm',
|
|
31
|
+
description: 'A reusable create/edit form from generated web fields and resolved policy, including advanced groups and a per-field custom renderer.',
|
|
32
|
+
loadComponent: loadObjectForm,
|
|
33
|
+
order: 2,
|
|
34
|
+
tags: ['forms', 'fields', 'object-form', 'registry'],
|
|
35
|
+
modes: { mock: { label: 'Mock' } },
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Browser-safe view helpers for the field-policy settings catalog. */
|
|
2
|
+
import type { FieldPolicyDetailItem, FieldPolicySettingsCatalogData, FieldPolicySettingsCatalogPage, FieldPolicySummaryItem } from '../settings-catalog.js';
|
|
3
|
+
import type { FieldPolicyAuditSnapshot, FieldPolicyDriftRow, FieldPolicyVisibility, ResolvedFieldPolicy } from '../types.js';
|
|
4
|
+
import type { FieldPolicyEditorAdapter } from './field-policy-editor.js';
|
|
5
|
+
export declare const MAX_FIELD_POLICY_AUDIT_OBJECT_REFS = 100;
|
|
6
|
+
export interface FieldPolicyControlPanelAdapter extends FieldPolicyEditorAdapter {
|
|
7
|
+
loadAudit(input?: {
|
|
8
|
+
objectRefs?: string[];
|
|
9
|
+
countObjectRefs?: string[];
|
|
10
|
+
includeDrift?: boolean;
|
|
11
|
+
}): Promise<FieldPolicyAuditSnapshot>;
|
|
12
|
+
}
|
|
13
|
+
/** Keeps every catalog navigation path on the caller-selected page size. */
|
|
14
|
+
export declare function fieldPolicyCatalogPreservedParams(data: Pick<FieldPolicySettingsCatalogData, 'page' | 'filters'>): Record<string, string>;
|
|
15
|
+
export interface FieldPolicyLayerCell {
|
|
16
|
+
contributed: boolean;
|
|
17
|
+
defaultValue: unknown;
|
|
18
|
+
hasDefault: boolean;
|
|
19
|
+
locked: boolean;
|
|
20
|
+
visibility: FieldPolicyVisibility;
|
|
21
|
+
visibilityForced: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface FieldPolicyFieldRollup {
|
|
24
|
+
known: boolean;
|
|
25
|
+
code: FieldPolicyLayerCell;
|
|
26
|
+
app: FieldPolicyLayerCell;
|
|
27
|
+
org: FieldPolicyLayerCell;
|
|
28
|
+
userCount: number;
|
|
29
|
+
orgRow?: FieldPolicyAuditSnapshot['orgRows'][number];
|
|
30
|
+
appRow?: FieldPolicyAuditSnapshot['appRows'][number];
|
|
31
|
+
resolved?: ResolvedFieldPolicy;
|
|
32
|
+
}
|
|
33
|
+
export declare function auditObjectRefs(page: Pick<FieldPolicySettingsCatalogPage, 'items' | 'selected'>): {
|
|
34
|
+
objectRefs: string[];
|
|
35
|
+
countObjectRefs: string[];
|
|
36
|
+
};
|
|
37
|
+
export declare function fieldPolicyRollup(audit: FieldPolicyAuditSnapshot, objectRef: string, fieldName: string): FieldPolicyFieldRollup;
|
|
38
|
+
export declare function decorateCatalogPage(page: FieldPolicySettingsCatalogPage, audit: FieldPolicyAuditSnapshot): FieldPolicySettingsCatalogPage;
|
|
39
|
+
export declare function decorateCatalogItem<T extends FieldPolicySummaryItem>(item: T, audit: FieldPolicyAuditSnapshot): Omit<T, 'label' | 'description' | 'status'> & FieldPolicySummaryItem;
|
|
40
|
+
export declare function orgRowIdsForObject(audit: FieldPolicyAuditSnapshot, objectRef: string): string[];
|
|
41
|
+
export declare function prunableDriftRows(audit: FieldPolicyAuditSnapshot): FieldPolicyDriftRow[];
|
|
42
|
+
/** Structural tenant-nav seam; hosts supply it to AdminShell's tenant edge. */
|
|
43
|
+
export declare function fieldPolicyControlPanelNavItem(options: {
|
|
44
|
+
href: string;
|
|
45
|
+
permissions?: readonly string[];
|
|
46
|
+
label?: string;
|
|
47
|
+
icon?: string;
|
|
48
|
+
}): {
|
|
49
|
+
href: string;
|
|
50
|
+
label: string;
|
|
51
|
+
icon?: string;
|
|
52
|
+
} | null;
|
|
53
|
+
export type { FieldPolicyDetailItem };
|
|
54
|
+
//# sourceMappingURL=settings-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-catalog.d.ts","sourceRoot":"","sources":["../../src/svelte/settings-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** Browser-safe view helpers for the field-policy settings catalog. */
|
|
2
|
+
export const MAX_FIELD_POLICY_AUDIT_OBJECT_REFS = 100;
|
|
3
|
+
/** Keeps every catalog navigation path on the caller-selected page size. */
|
|
4
|
+
export function fieldPolicyCatalogPreservedParams(data) {
|
|
5
|
+
return {
|
|
6
|
+
...(data.filters.packageFilter
|
|
7
|
+
? { package: data.filters.packageFilter }
|
|
8
|
+
: {}),
|
|
9
|
+
...(data.filters.objectFilter ? { object: data.filters.objectFilter } : {}),
|
|
10
|
+
...(data.filters.customizedOnly ? { customized: '1' } : {}),
|
|
11
|
+
pageSize: String(data.page.pageSize),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function auditObjectRefs(page) {
|
|
15
|
+
const countObjectRefs = [
|
|
16
|
+
...new Set([
|
|
17
|
+
...(page.selected ? [page.selected.objectRef] : []),
|
|
18
|
+
...page.items.map((item) => item.objectRef),
|
|
19
|
+
]),
|
|
20
|
+
];
|
|
21
|
+
return {
|
|
22
|
+
objectRefs: countObjectRefs.slice(0, MAX_FIELD_POLICY_AUDIT_OBJECT_REFS),
|
|
23
|
+
countObjectRefs,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function fieldPolicyRollup(audit, objectRef, fieldName) {
|
|
27
|
+
const policy = audit.policies[objectRef];
|
|
28
|
+
const resolved = policy?.fields[fieldName];
|
|
29
|
+
const layers = policy?.layers[fieldName] ?? [];
|
|
30
|
+
const cell = (tiers, own) => {
|
|
31
|
+
let defaultValue;
|
|
32
|
+
let hasDefault = false;
|
|
33
|
+
let visibility = 'basic';
|
|
34
|
+
let locked = false;
|
|
35
|
+
const accepted = layers.filter((layer) => tiers.includes(layer.layer));
|
|
36
|
+
for (const layer of accepted) {
|
|
37
|
+
if (layer.delta.default) {
|
|
38
|
+
hasDefault = true;
|
|
39
|
+
defaultValue = layer.delta.default.value;
|
|
40
|
+
}
|
|
41
|
+
if (layer.delta.visibility)
|
|
42
|
+
visibility = layer.delta.visibility;
|
|
43
|
+
if (layer.delta.locked !== undefined)
|
|
44
|
+
locked = layer.delta.locked;
|
|
45
|
+
}
|
|
46
|
+
const visibilityForced = resolved?.visibilityForced === true && visibility !== 'basic';
|
|
47
|
+
return {
|
|
48
|
+
contributed: accepted.some((layer) => layer.layer === own),
|
|
49
|
+
defaultValue,
|
|
50
|
+
hasDefault,
|
|
51
|
+
visibility: visibilityForced ? 'basic' : visibility,
|
|
52
|
+
locked,
|
|
53
|
+
visibilityForced,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
known: resolved !== undefined,
|
|
58
|
+
code: cell(['code'], 'code'),
|
|
59
|
+
app: cell(['code', 'app'], 'app'),
|
|
60
|
+
org: cell(['code', 'app', 'tenant'], 'tenant'),
|
|
61
|
+
userCount: audit.userOverrideCounts[objectRef]?.[fieldName] ?? 0,
|
|
62
|
+
orgRow: audit.orgRows.find((row) => row.objectRef === objectRef && row.fieldName === fieldName),
|
|
63
|
+
appRow: audit.appRows.find((row) => row.objectRef === objectRef && row.fieldName === fieldName),
|
|
64
|
+
...(resolved ? { resolved } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function decorateCatalogPage(page, audit) {
|
|
68
|
+
return {
|
|
69
|
+
...page,
|
|
70
|
+
items: page.items.map((item) => decorateCatalogItem(item, audit)),
|
|
71
|
+
selected: page.selected ? decorateCatalogItem(page.selected, audit) : null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function decorateCatalogItem(item, audit) {
|
|
75
|
+
const rollup = fieldPolicyRollup(audit, item.objectRef, item.fieldName);
|
|
76
|
+
const status = [
|
|
77
|
+
rollup.app.contributed || rollup.appRow ? 'App' : null,
|
|
78
|
+
rollup.org.contributed || rollup.orgRow ? 'Organization' : null,
|
|
79
|
+
rollup.userCount
|
|
80
|
+
? `${rollup.userCount} user override${rollup.userCount === 1 ? '' : 's'}`
|
|
81
|
+
: null,
|
|
82
|
+
]
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.join(' · ');
|
|
85
|
+
return {
|
|
86
|
+
...item,
|
|
87
|
+
label: rollup.resolved?.label ?? humanize(item.fieldName),
|
|
88
|
+
description: rollup.resolved?.help ?? item.description,
|
|
89
|
+
...(status ? { status } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function orgRowIdsForObject(audit, objectRef) {
|
|
93
|
+
return audit.orgRows
|
|
94
|
+
.filter((row) => row.objectRef === objectRef)
|
|
95
|
+
.map((row) => row.id);
|
|
96
|
+
}
|
|
97
|
+
export function prunableDriftRows(audit) {
|
|
98
|
+
return audit.driftRows.filter((row) => row.prunable);
|
|
99
|
+
}
|
|
100
|
+
/** Structural tenant-nav seam; hosts supply it to AdminShell's tenant edge. */
|
|
101
|
+
export function fieldPolicyControlPanelNavItem(options) {
|
|
102
|
+
if (options.permissions &&
|
|
103
|
+
!options.permissions.includes('fields.policy.manage'))
|
|
104
|
+
return null;
|
|
105
|
+
return {
|
|
106
|
+
href: options.href,
|
|
107
|
+
label: options.label ?? 'Field settings',
|
|
108
|
+
...(options.icon ? { icon: options.icon } : {}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function humanize(value) {
|
|
112
|
+
return value
|
|
113
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
114
|
+
.replace(/[_-]+/g, ' ')
|
|
115
|
+
.replace(/^./, (character) => character.toUpperCase());
|
|
116
|
+
}
|