@vectoriox/iox-ui 5.26.0 → 5.28.0

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 (30) hide show
  1. package/design-core/index.d.ts +144 -4
  2. package/fesm2022/vectoriox-iox-ui-design-core.mjs +153 -5
  3. package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
  4. package/fesm2022/vectoriox-iox-ui-forms-core.mjs +40 -5
  5. package/fesm2022/vectoriox-iox-ui-forms-core.mjs.map +1 -1
  6. package/fesm2022/vectoriox-iox-ui-generation-core.mjs +7018 -0
  7. package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -0
  8. package/fesm2022/vectoriox-iox-ui.mjs +72 -14
  9. package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
  10. package/forms-core/index.d.ts +18 -2
  11. package/generation-core/index.d.ts +1397 -0
  12. package/generation-core/package.json +3 -0
  13. package/index.d.ts +13 -2
  14. package/package.json +5 -1
  15. package/src/design-core/flavor-spec.generated.ts +153 -5
  16. package/src/forms-core/collect.ts +3 -1
  17. package/src/forms-core/options.ts +29 -0
  18. package/src/forms-core/public-api.ts +1 -0
  19. package/src/forms-core/transport.ts +6 -2
  20. package/src/generation-core/compose-site.ts +474 -0
  21. package/src/generation-core/node-baseline.model.ts +10 -0
  22. package/src/generation-core/node-baselines.generated.ts +4841 -0
  23. package/src/generation-core/patterns.ts +125 -0
  24. package/src/generation-core/public-api.ts +20 -0
  25. package/src/generation-core/serialized-node.ts +91 -0
  26. package/src/generation-core/site-plan.model.ts +136 -0
  27. package/src/generation-core/site-spec.generated.ts +1111 -0
  28. package/src/generation-core/templates.ts +60 -0
  29. package/src/generation-core/validate-site-plan.ts +413 -0
  30. package/styles/iox-forms.css +136 -8
@@ -0,0 +1,3 @@
1
+ {
2
+ "module": "../fesm2022/vectoriox-iox-ui-generation-core.mjs"
3
+ }
package/index.d.ts CHANGED
@@ -1189,7 +1189,17 @@ declare class IoxFieldComponent implements OnInit, OnDestroy {
1189
1189
  rangeMax: number | string;
1190
1190
  rangeStep: number | string;
1191
1191
  accept: string;
1192
- options: FieldOption[];
1192
+ /** Explicit choices (bound data, a hand-built node). Wins over `optionsText`. */
1193
+ set options(value: FieldOption[] | null | undefined);
1194
+ get options(): FieldOption[];
1195
+ /** The author's choices, one per line (`value|Label`) — the Field's trait. A SETTER: the builder
1196
+ * assigns traits directly, with no ngOnChanges. */
1197
+ set optionsText(value: string | null | undefined);
1198
+ get optionsText(): string;
1199
+ /** What the radio group / dropdown renders: the shared forms-core reading of the two inputs above. */
1200
+ choices: FieldOption[];
1201
+ private _options;
1202
+ private _optionsText;
1193
1203
  value: any;
1194
1204
  /** Rules beyond `required` — carried on the node, interpreted by forms-core. */
1195
1205
  rules: ValidationRule[];
@@ -1225,6 +1235,7 @@ declare class IoxFieldComponent implements OnInit, OnDestroy {
1225
1235
  /** The message a visitor reads. Translation is the consumer's, via IOX_FORM_MESSAGES. */
1226
1236
  get errorText(): string;
1227
1237
  private toSpec;
1238
+ private refreshChoices;
1228
1239
  /** `file`, `date`, `number`… map straight through; everything else is a text input. */
1229
1240
  get inputType(): string;
1230
1241
  /** Help and error are BOTH announced, in that order, when both are present. */
@@ -1232,7 +1243,7 @@ declare class IoxFieldComponent implements OnInit, OnDestroy {
1232
1243
  emit(value: any): void;
1233
1244
  touch(): void;
1234
1245
  static ɵfac: i0.ɵɵFactoryDeclaration<IoxFieldComponent, [{ optional: true; }, null]>;
1235
- static ɵcmp: i0.ɵɵComponentDeclaration<IoxFieldComponent, "div[iox-field]", never, { "nodeId": { "alias": "nodeId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "label": { "alias": "label"; "required": false; }; "control": { "alias": "control"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "help": { "alias": "help"; "required": false; }; "required": { "alias": "required"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "labelHidden": { "alias": "labelHidden"; "required": false; }; "requiredMark": { "alias": "requiredMark"; "required": false; }; "rows": { "alias": "rows"; "required": false; }; "rangeMin": { "alias": "rangeMin"; "required": false; }; "rangeMax": { "alias": "rangeMax"; "required": false; }; "rangeStep": { "alias": "rangeStep"; "required": false; }; "accept": { "alias": "accept"; "required": false; }; "options": { "alias": "options"; "required": false; }; "value": { "alias": "value"; "required": false; }; "rules": { "alias": "rules"; "required": false; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; }; "designErrorText": { "alias": "designErrorText"; "required": false; }; "showValid": { "alias": "showValid"; "required": false; }; }, { "valueChange": "valueChange"; "touched": "touched"; }, never, never, false, never>;
1246
+ static ɵcmp: i0.ɵɵComponentDeclaration<IoxFieldComponent, "div[iox-field]", never, { "nodeId": { "alias": "nodeId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "label": { "alias": "label"; "required": false; }; "control": { "alias": "control"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "help": { "alias": "help"; "required": false; }; "required": { "alias": "required"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "labelHidden": { "alias": "labelHidden"; "required": false; }; "requiredMark": { "alias": "requiredMark"; "required": false; }; "rows": { "alias": "rows"; "required": false; }; "rangeMin": { "alias": "rangeMin"; "required": false; }; "rangeMax": { "alias": "rangeMax"; "required": false; }; "rangeStep": { "alias": "rangeStep"; "required": false; }; "accept": { "alias": "accept"; "required": false; }; "options": { "alias": "options"; "required": false; }; "optionsText": { "alias": "optionsText"; "required": false; }; "value": { "alias": "value"; "required": false; }; "rules": { "alias": "rules"; "required": false; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; }; "designErrorText": { "alias": "designErrorText"; "required": false; }; "showValid": { "alias": "showValid"; "required": false; }; }, { "valueChange": "valueChange"; "touched": "touched"; }, never, never, false, never>;
1236
1247
  }
1237
1248
 
1238
1249
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.26.0",
3
+ "version": "5.28.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -35,6 +35,10 @@
35
35
  "types": "./forms-core/index.d.ts",
36
36
  "default": "./fesm2022/vectoriox-iox-ui-forms-core.mjs"
37
37
  },
38
+ "./generation-core": {
39
+ "types": "./generation-core/index.d.ts",
40
+ "default": "./fesm2022/vectoriox-iox-ui-generation-core.mjs"
41
+ },
38
42
  "./seo-core": {
39
43
  "types": "./seo-core/index.d.ts",
40
44
  "default": "./fesm2022/vectoriox-iox-ui-seo-core.mjs"
@@ -1811,7 +1811,8 @@ export const SPEC_FIELD_CLASS_BASELINE = {
1811
1811
  ".iox-es-field .iox-field__control:focus-visible": {
1812
1812
  "border-color": "var(--iox-accent-text)",
1813
1813
  "box-shadow": "var(--iox-field-focus)",
1814
- "outline": "none"
1814
+ "outline": "2px solid transparent",
1815
+ "outline-offset": "2px"
1815
1816
  },
1816
1817
  ".iox-es-field .iox-field__label": {
1817
1818
  "font-family": "var(--iox-font-body)",
@@ -1819,6 +1820,12 @@ export const SPEC_FIELD_CLASS_BASELINE = {
1819
1820
  "font-weight": "var(--iox-field-label-weight)",
1820
1821
  "color": "var(--iox-text)"
1821
1822
  },
1823
+ ".iox-es-field .iox-field__option": {
1824
+ "font-family": "var(--iox-font-body)",
1825
+ "font-size": "var(--iox-size-body)",
1826
+ "color": "var(--iox-text)",
1827
+ "cursor": "pointer"
1828
+ },
1822
1829
  ".iox-es-field .iox-field__help": {
1823
1830
  "font-size": "var(--iox-size-small)",
1824
1831
  "color": "var(--iox-text-muted)"
@@ -1834,11 +1841,121 @@ export const SPEC_FIELD_CLASS_BASELINE = {
1834
1841
  "color": "var(--iox-text-muted)",
1835
1842
  "opacity": "1"
1836
1843
  },
1837
- ".iox-es-field .iox-field__control:where([type='checkbox'], [type='radio'], [type='range'])": {
1844
+ ".iox-es-field .iox-field__control:disabled": {
1845
+ "opacity": "0.55",
1846
+ "cursor": "not-allowed"
1847
+ },
1848
+ ".iox-es-field .iox-field__control:where(.iox-field__control--switch, [type='range'])": {
1838
1849
  "border": "none",
1839
1850
  "padding": "0",
1840
- "background": "transparent",
1841
- "accent-color": "var(--iox-accent-text)"
1851
+ "background": "transparent"
1852
+ },
1853
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio'])": {
1854
+ "appearance": "none",
1855
+ "-webkit-appearance": "none",
1856
+ "display": "inline-grid",
1857
+ "place-content": "center",
1858
+ "flex-shrink": "0",
1859
+ "margin": "0",
1860
+ "padding": "0",
1861
+ "inline-size": "1.25em",
1862
+ "block-size": "1.25em",
1863
+ "vertical-align": "middle",
1864
+ "cursor": "pointer",
1865
+ "background": "var(--iox-surface)",
1866
+ "border-color": "var(--iox-line-strong)",
1867
+ "border-radius": "var(--iox-radius-xs)",
1868
+ "transition": "background var(--iox-dur-fast) var(--iox-ease-standard), border-color var(--iox-dur-fast) var(--iox-ease-standard), box-shadow var(--iox-dur-fast) var(--iox-ease-standard)"
1869
+ },
1870
+ ".iox-es-field .iox-field__control[type='radio']": {
1871
+ "border-radius": "50%"
1872
+ },
1873
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio'])::before": {
1874
+ "content": "''",
1875
+ "inline-size": "0.28em",
1876
+ "block-size": "0.56em",
1877
+ "border-style": "solid",
1878
+ "border-color": "var(--iox-accent-ink)",
1879
+ "border-width": "0 0.14em 0.14em 0",
1880
+ "transform": "translateY(-0.08em) rotate(45deg) scale(0)",
1881
+ "transition": "transform var(--iox-dur-fast) var(--iox-ease-standard)"
1882
+ },
1883
+ ".iox-es-field .iox-field__control[type='radio']::before": {
1884
+ "inline-size": "0",
1885
+ "block-size": "0",
1886
+ "border-width": "0.25em",
1887
+ "border-radius": "50%",
1888
+ "transform": "scale(0)"
1889
+ },
1890
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio']):hover": {
1891
+ "border-color": "var(--iox-accent-text)"
1892
+ },
1893
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio']):checked": {
1894
+ "background": "var(--iox-accent)",
1895
+ "border-color": "var(--iox-accent-text)"
1896
+ },
1897
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio']):checked:hover": {
1898
+ "background": "var(--iox-accent-hover)"
1899
+ },
1900
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio']):checked::before": {
1901
+ "transform": "translateY(-0.08em) rotate(45deg) scale(1)"
1902
+ },
1903
+ ".iox-es-field .iox-field__control[type='radio']:checked::before": {
1904
+ "transform": "scale(1)"
1905
+ },
1906
+ ".iox-es-field .iox-field__control:where([type='checkbox']:not(.iox-field__control--switch), [type='radio']):focus-visible": {
1907
+ "box-shadow": "var(--iox-focus-ring)"
1908
+ },
1909
+ ".iox-es-field .iox-field__control:where([type='range'])": {
1910
+ "appearance": "none",
1911
+ "-webkit-appearance": "none",
1912
+ "inline-size": "100%",
1913
+ "block-size": "1.25em",
1914
+ "cursor": "pointer"
1915
+ },
1916
+ ".iox-es-field .iox-field__control:where([type='range'])::-webkit-slider-runnable-track": {
1917
+ "block-size": "0.25em",
1918
+ "border-radius": "var(--iox-radius-pill)",
1919
+ "background": "var(--iox-line-strong)",
1920
+ "outline": "1px solid transparent"
1921
+ },
1922
+ ".iox-es-field .iox-field__control:where([type='range'])::-moz-range-track": {
1923
+ "block-size": "0.25em",
1924
+ "border-radius": "var(--iox-radius-pill)",
1925
+ "background": "var(--iox-line-strong)",
1926
+ "outline": "1px solid transparent"
1927
+ },
1928
+ ".iox-es-field .iox-field__control:where([type='range'])::-webkit-slider-thumb": {
1929
+ "appearance": "none",
1930
+ "-webkit-appearance": "none",
1931
+ "inline-size": "1.25em",
1932
+ "block-size": "1.25em",
1933
+ "border-radius": "50%",
1934
+ "background": "var(--iox-accent-text)",
1935
+ "border": "2px solid var(--iox-bg)",
1936
+ "box-sizing": "border-box",
1937
+ "cursor": "pointer",
1938
+ "margin-block-start": "-0.5em"
1939
+ },
1940
+ ".iox-es-field .iox-field__control:where([type='range'])::-moz-range-thumb": {
1941
+ "appearance": "none",
1942
+ "-webkit-appearance": "none",
1943
+ "inline-size": "1.25em",
1944
+ "block-size": "1.25em",
1945
+ "border-radius": "50%",
1946
+ "background": "var(--iox-accent-text)",
1947
+ "border": "2px solid var(--iox-bg)",
1948
+ "box-sizing": "border-box",
1949
+ "cursor": "pointer"
1950
+ },
1951
+ ".iox-es-field .iox-field__control:where([type='range']):focus-visible": {
1952
+ "box-shadow": "none"
1953
+ },
1954
+ ".iox-es-field .iox-field__control:where([type='range']):focus-visible::-webkit-slider-thumb": {
1955
+ "box-shadow": "var(--iox-focus-ring)"
1956
+ },
1957
+ ".iox-es-field .iox-field__control:where([type='range']):focus-visible::-moz-range-thumb": {
1958
+ "box-shadow": "var(--iox-focus-ring)"
1842
1959
  },
1843
1960
  ".iox-es-field .iox-field__control--switch": {
1844
1961
  "background": "var(--iox-text-muted)",
@@ -2044,9 +2161,40 @@ export const SPEC_CONTRAST_AUDIT = {
2044
2161
  "min": 3
2045
2162
  },
2046
2163
  {
2047
- "part": "checkbox · radio · range",
2164
+ "part": "checkbox · radio edge",
2165
+ "fg": "var(--iox-line-strong)",
2166
+ "min": 3
2167
+ },
2168
+ {
2169
+ "part": "checkbox · radio edge on its fill",
2170
+ "fg": "var(--iox-line-strong)",
2171
+ "on": "var(--iox-surface)",
2172
+ "min": 3
2173
+ },
2174
+ {
2175
+ "part": "checkbox · radio checked",
2176
+ "fg": [
2177
+ "var(--iox-accent-text)",
2178
+ "var(--iox-accent)"
2179
+ ],
2180
+ "any": true,
2181
+ "min": 3
2182
+ },
2183
+ {
2184
+ "part": "check mark · radio dot",
2185
+ "fg": "var(--iox-accent-ink)",
2186
+ "on": "var(--iox-accent)",
2187
+ "min": 3
2188
+ },
2189
+ {
2190
+ "part": "hover edge · range thumb",
2048
2191
  "fg": "var(--iox-accent-text)",
2049
2192
  "min": 3
2193
+ },
2194
+ {
2195
+ "part": "range track",
2196
+ "fg": "var(--iox-line-strong)",
2197
+ "min": 3
2050
2198
  }
2051
2199
  ]
2052
2200
  } as const;
@@ -15,6 +15,7 @@ import {
15
15
  SubmitAction,
16
16
  ValidationRule,
17
17
  } from './form.model';
18
+ import { fieldOptionsOf } from './options';
18
19
 
19
20
  /** The minimum a node must look like. Structural on purpose: this file knows no renderer. */
20
21
  export interface FormNodeLike {
@@ -117,7 +118,8 @@ function toFieldSpec(node: FormNodeLike, taken: ReadonlySet<string>): FieldSpec
117
118
  if (props['required']) spec.required = true;
118
119
  const rules = normaliseRules(props['rules']);
119
120
  if (rules.length) spec.rules = rules;
120
- if (Array.isArray(props['options']) && props['options'].length) spec.options = props['options'];
121
+ const options = fieldOptionsOf(props);
122
+ if (options.length) spec.options = options;
121
123
  if (props['optionsSource']) spec.optionsSource = props['optionsSource'];
122
124
  if (props['defaultValue'] !== undefined && props['defaultValue'] !== '') {
123
125
  spec.defaultValue = props['defaultValue'];
@@ -0,0 +1,29 @@
1
+ import { FieldOption } from './form.model';
2
+
3
+ /** `value|Label` separates a submitted value from what the visitor reads; a bare line is both. */
4
+ const OPTION_SEPARATOR = '|';
5
+
6
+ /**
7
+ * A Field's `optionsText` trait — the author's choices, one per line — as options. ONE parser for the
8
+ * builder canvas, both engine paths and iox-cms: until 2026-10-19 nothing parsed the trait, so a Radio
9
+ * group or a Dropdown from the tray rendered no choices at all on the canvas and on the live site.
10
+ */
11
+ export function parseFieldOptions(text: unknown): FieldOption[] {
12
+ if (typeof text !== 'string') return [];
13
+ const out: FieldOption[] = [];
14
+ for (const line of text.split(/\r?\n/)) {
15
+ const raw = line.trim();
16
+ if (!raw) continue;
17
+ const at = raw.indexOf(OPTION_SEPARATOR);
18
+ const value = (at === -1 ? raw : raw.slice(0, at)).trim();
19
+ const label = (at === -1 ? raw : raw.slice(at + 1)).trim() || value;
20
+ if (value) out.push({ value, label });
21
+ }
22
+ return out;
23
+ }
24
+
25
+ /** The choices a saved Field offers: an explicit `options` list wins, else its `optionsText` parsed. */
26
+ export function fieldOptionsOf(props: { options?: unknown; optionsText?: unknown } | null | undefined): FieldOption[] {
27
+ if (Array.isArray(props?.options) && props!.options.length) return props!.options as FieldOption[];
28
+ return parseFieldOptions(props?.optionsText);
29
+ }
@@ -14,6 +14,7 @@ export * from './form.model';
14
14
  export * from './messages';
15
15
  export * from './validate';
16
16
  export * from './collect';
17
+ export * from './options';
17
18
  export * from './runtime';
18
19
  export * from './rule-catalog';
19
20
  export * from './transport';
@@ -49,10 +49,14 @@ export type FormTransport = (request: SubmitRequest) => Promise<SubmitResponse>;
49
49
  * The public submit path. Built here rather than interpolated at each call site, so the engine and
50
50
  * any future host cannot disagree about it — and so changing it is one edit.
51
51
  *
52
- * Deliberately relative and WITHOUT a leading slash: the engine mounts under a per-org base href.
52
+ * ⚠️ ROOT-relative (leading slash), never base-relative. A live site is served under a per-org
53
+ * `<base href="/<label>/">`, so a relative `api/…` resolved to `/<label>/api/…` — a path the reverse
54
+ * proxy (`/api` → iox-cms, everything else → the engine) hands to the ENGINE, which answers a POST with
55
+ * 404: no visitor submission ever reached iox-cms from an org path (found 2026-10-19 by
56
+ * e2e:forminsert:real). The org is already in the path segment after `site/`.
53
57
  */
54
58
  export function submitEndpointPath(orgId: string, formId: string): string {
55
- return `api/site/${encodeURIComponent(orgId)}/forms/${encodeURIComponent(formId)}/submit`;
59
+ return `/api/site/${encodeURIComponent(orgId)}/forms/${encodeURIComponent(formId)}/submit`;
56
60
  }
57
61
 
58
62
  /**