@ds-mo/ui 8.3.1 → 9.1.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 (184) hide show
  1. package/README.md +28 -3
  2. package/dist/.build-stamp +1 -1
  3. package/dist/agent-patterns.json +233 -0
  4. package/dist/agent.json +155 -174
  5. package/dist/angular/ds-menu.d.ts +6 -1
  6. package/dist/angular/ds-menu.js +7 -4
  7. package/dist/angular/ds-swatch-picker.d.ts +15 -0
  8. package/dist/angular/{ds-pagination.js → ds-swatch-picker.js} +15 -15
  9. package/dist/angular/index.d.ts +1 -1
  10. package/dist/angular/index.js +1 -2
  11. package/dist/angular/proxies.d.ts +1 -2
  12. package/dist/angular/proxies.js +1 -2
  13. package/dist/components/ds-app-shell.js +1 -1
  14. package/dist/components/ds-badge.js +1 -1
  15. package/dist/components/ds-banner.js +1 -1
  16. package/dist/components/ds-bar-nav.js +1 -1
  17. package/dist/components/ds-bar-nav.js.map +1 -1
  18. package/dist/components/ds-button-filled.js +1 -1
  19. package/dist/components/ds-button-unfilled.js +1 -1
  20. package/dist/components/ds-card-data-viz-donut.js +1 -1
  21. package/dist/components/ds-card-setting.js +1 -1
  22. package/dist/components/ds-card-shell-data-viz.js +1 -1
  23. package/dist/components/ds-card.js +1 -1
  24. package/dist/components/ds-chart-bar.js +1 -1
  25. package/dist/components/ds-chart-bar.js.map +1 -1
  26. package/dist/components/ds-chart-donut.js +1 -1
  27. package/dist/components/ds-chart-legend.js +1 -1
  28. package/dist/components/ds-chart-line.js +1 -1
  29. package/dist/components/ds-checkbox.js +1 -1
  30. package/dist/components/ds-chip.js +1 -1
  31. package/dist/components/ds-empty-state.js +1 -1
  32. package/dist/components/ds-field.js +1 -1
  33. package/dist/components/ds-input.js +1 -1
  34. package/dist/components/ds-menu.js +1 -1
  35. package/dist/components/ds-modal.js +1 -1
  36. package/dist/components/ds-panel-nav.js +1 -1
  37. package/dist/components/ds-panel-nav.js.map +1 -1
  38. package/dist/components/ds-panel-sub-nav.js +1 -1
  39. package/dist/components/ds-panel-tools.js +1 -1
  40. package/dist/components/ds-radio.js +1 -1
  41. package/dist/components/ds-select-multi.js +1 -1
  42. package/dist/components/ds-select-multi.js.map +1 -1
  43. package/dist/components/ds-select.js +1 -1
  44. package/dist/components/ds-select.js.map +1 -1
  45. package/dist/components/ds-shell-gradient-picker.js +1 -1
  46. package/dist/components/ds-shell-gradient-picker.js.map +1 -1
  47. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  48. package/dist/components/ds-shell-gradient-swatch.js.map +1 -1
  49. package/dist/components/ds-skeleton.js +1 -1
  50. package/dist/components/ds-skeleton.js.map +1 -1
  51. package/dist/components/ds-slider.js +1 -1
  52. package/dist/components/ds-slider.js.map +1 -1
  53. package/dist/components/{ds-pagination.d.ts → ds-swatch-picker.d.ts} +4 -4
  54. package/dist/components/ds-swatch-picker.js +2 -0
  55. package/dist/components/ds-swatch-picker.js.map +1 -0
  56. package/dist/components/ds-tab-group.js +1 -1
  57. package/dist/components/ds-tag.js +1 -1
  58. package/dist/components/ds-text.js +1 -1
  59. package/dist/components/ds-tooltip-data-viz.js +1 -1
  60. package/dist/components/ds-tooltip.js +1 -1
  61. package/dist/components/{p-CysJosvS.js → p-B-zhgAvL.js} +2 -2
  62. package/dist/components/{p-C21sIElQ.js → p-BLzivrE3.js} +2 -2
  63. package/dist/components/{p-DAuadHYu.js → p-BbV68F23.js} +2 -2
  64. package/dist/components/{p-_y99wWNU.js → p-C7RObioO.js} +2 -2
  65. package/dist/components/{p-DxQ4gzbA.js → p-CZ9wemOA.js} +2 -2
  66. package/dist/components/{p-BKA6houZ.js → p-CbS_WWs2.js} +2 -2
  67. package/dist/components/p-D1HFMTdC.js +2 -0
  68. package/dist/components/p-D1HFMTdC.js.map +1 -0
  69. package/dist/components/{p-BGcSrHKl.js → p-DT2MK8Kt.js} +2 -2
  70. package/dist/components/{p-DV23KMU-.js → p-DyZj3k9S.js} +2 -2
  71. package/dist/components/p-HBIxvS5C.js +2 -0
  72. package/dist/components/p-HBIxvS5C.js.map +1 -0
  73. package/dist/components/{p-D6hvtWDk.js → p-KRGBGTu5.js} +2 -2
  74. package/dist/components/p-l6PmnW_P.js +2 -0
  75. package/dist/components/p-l6PmnW_P.js.map +1 -0
  76. package/dist/components/p-tQ1QHeOE.js +2 -0
  77. package/dist/components/p-tQ1QHeOE.js.map +1 -0
  78. package/dist/docs/components.json +546 -1024
  79. package/dist/lib/components/Menu/menu-placement.d.ts +6 -3
  80. package/dist/lib/shell/index.d.ts +2 -2
  81. package/dist/lib/shell/index.js +40 -39
  82. package/dist/lib/shell/index.js.map +3 -3
  83. package/dist/lib/shell/shell-gradient-presets.d.ts +15 -0
  84. package/dist/lib/utils/choice-popup-alignment.d.ts +20 -0
  85. package/dist/lib/utils/index.d.ts +2 -0
  86. package/dist/lib/utils/index.js +16 -0
  87. package/dist/lib/utils/index.js.map +3 -3
  88. package/dist/mcp/cli.js +31409 -0
  89. package/dist/mcp-data/registry/app-shell.json +210 -0
  90. package/dist/mcp-data/registry/badge.json +289 -0
  91. package/dist/mcp-data/registry/banner.json +206 -0
  92. package/dist/mcp-data/registry/bar-nav.json +341 -0
  93. package/dist/mcp-data/registry/button-filled.json +386 -0
  94. package/dist/mcp-data/registry/button-unfilled.json +537 -0
  95. package/dist/mcp-data/registry/card-data-viz-donut.json +143 -0
  96. package/dist/mcp-data/registry/card-setting.json +194 -0
  97. package/dist/mcp-data/registry/card-shell-data-viz.json +173 -0
  98. package/dist/mcp-data/registry/card.json +126 -0
  99. package/dist/mcp-data/registry/chart-bar.json +121 -0
  100. package/dist/mcp-data/registry/chart-donut.json +257 -0
  101. package/dist/mcp-data/registry/chart-legend.json +209 -0
  102. package/dist/mcp-data/registry/chart-line.json +153 -0
  103. package/dist/mcp-data/registry/checkbox.json +361 -0
  104. package/dist/mcp-data/registry/chip.json +276 -0
  105. package/dist/mcp-data/registry/divider.json +225 -0
  106. package/dist/mcp-data/registry/empty-state.json +197 -0
  107. package/dist/mcp-data/registry/field.json +222 -0
  108. package/dist/mcp-data/registry/icon.json +242 -0
  109. package/dist/mcp-data/registry/input.json +597 -0
  110. package/dist/mcp-data/registry/loader.json +192 -0
  111. package/dist/mcp-data/registry/menu.json +459 -0
  112. package/dist/mcp-data/registry/modal.json +150 -0
  113. package/dist/mcp-data/registry/panel-nav.json +622 -0
  114. package/dist/mcp-data/registry/panel-sub-nav.json +249 -0
  115. package/dist/mcp-data/registry/panel-tools.json +374 -0
  116. package/dist/mcp-data/registry/radio.json +374 -0
  117. package/dist/mcp-data/registry/registry.json +5389 -0
  118. package/dist/mcp-data/registry/select-multi.json +755 -0
  119. package/dist/mcp-data/registry/select.json +740 -0
  120. package/dist/mcp-data/registry/shell-gradient-picker.json +195 -0
  121. package/dist/mcp-data/registry/shell-gradient-swatch.json +215 -0
  122. package/dist/mcp-data/registry/skeleton.json +282 -0
  123. package/dist/mcp-data/registry/slider.json +657 -0
  124. package/dist/mcp-data/registry/swatch-picker.json +231 -0
  125. package/dist/mcp-data/registry/switch.json +361 -0
  126. package/dist/mcp-data/registry/tab-group.json +255 -0
  127. package/dist/mcp-data/registry/tag.json +352 -0
  128. package/dist/mcp-data/registry/text.json +371 -0
  129. package/dist/mcp-data/registry/tooltip-data-viz.json +158 -0
  130. package/dist/mcp-data/registry/tooltip.json +311 -0
  131. package/dist/react/components.d.ts +1 -2
  132. package/dist/react/components.js +1 -2
  133. package/dist/react/ds-menu.d.ts +1 -0
  134. package/dist/react/ds-menu.js +2 -1
  135. package/dist/react/ds-swatch-picker.d.ts +12 -0
  136. package/dist/react/ds-swatch-picker.js +12 -0
  137. package/dist/types/components/Menu/Menu.d.ts +7 -1
  138. package/dist/types/components/Menu/menu-placement.d.ts +6 -3
  139. package/dist/types/components/Menu/menu-types.d.ts +14 -1
  140. package/dist/types/components/ShellGradientPicker/ShellGradientPicker.d.ts +2 -5
  141. package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -0
  142. package/dist/types/components/SwatchPicker/SwatchPicker.d.ts +21 -0
  143. package/dist/types/components/SwatchPicker/swatch-picker-types.d.ts +26 -0
  144. package/dist/types/components.d.ts +117 -237
  145. package/dist/types/shell/index.d.ts +2 -2
  146. package/dist/types/shell/shell-gradient-presets.d.ts +15 -0
  147. package/dist/types/utils/choice-popup-alignment.d.ts +21 -0
  148. package/dist/types/utils/index.d.ts +2 -0
  149. package/package.json +6 -2
  150. package/dist/angular/ds-pagination.d.ts +0 -15
  151. package/dist/angular/ds-table.d.ts +0 -33
  152. package/dist/angular/ds-table.js +0 -48
  153. package/dist/components/ds-pagination.js +0 -2
  154. package/dist/components/ds-pagination.js.map +0 -1
  155. package/dist/components/ds-table.d.ts +0 -11
  156. package/dist/components/ds-table.js +0 -2
  157. package/dist/components/ds-table.js.map +0 -1
  158. package/dist/components/p-CKV6rOGa.js +0 -2
  159. package/dist/components/p-CKV6rOGa.js.map +0 -1
  160. package/dist/components/p-CSTr2132.js +0 -2
  161. package/dist/components/p-CSTr2132.js.map +0 -1
  162. package/dist/components/p-C_cu-HIT.js +0 -2
  163. package/dist/components/p-C_cu-HIT.js.map +0 -1
  164. package/dist/components/p-D0B9uDTP.js +0 -2
  165. package/dist/components/p-D0B9uDTP.js.map +0 -1
  166. package/dist/components/p-DhxWfdX0.js +0 -2
  167. package/dist/components/p-DhxWfdX0.js.map +0 -1
  168. package/dist/components/p-Pip-LNhl.js +0 -2
  169. package/dist/components/p-Pip-LNhl.js.map +0 -1
  170. package/dist/react/ds-pagination.d.ts +0 -12
  171. package/dist/react/ds-pagination.js +0 -12
  172. package/dist/react/ds-table.d.ts +0 -21
  173. package/dist/react/ds-table.js +0 -16
  174. package/dist/types/components/Pagination/Pagination.d.ts +0 -14
  175. package/dist/types/components/Table/Table.d.ts +0 -56
  176. /package/dist/components/{p-CysJosvS.js.map → p-B-zhgAvL.js.map} +0 -0
  177. /package/dist/components/{p-C21sIElQ.js.map → p-BLzivrE3.js.map} +0 -0
  178. /package/dist/components/{p-DAuadHYu.js.map → p-BbV68F23.js.map} +0 -0
  179. /package/dist/components/{p-_y99wWNU.js.map → p-C7RObioO.js.map} +0 -0
  180. /package/dist/components/{p-DxQ4gzbA.js.map → p-CZ9wemOA.js.map} +0 -0
  181. /package/dist/components/{p-BKA6houZ.js.map → p-CbS_WWs2.js.map} +0 -0
  182. /package/dist/components/{p-BGcSrHKl.js.map → p-DT2MK8Kt.js.map} +0 -0
  183. /package/dist/components/{p-DV23KMU-.js.map → p-DyZj3k9S.js.map} +0 -0
  184. /package/dist/components/{p-D6hvtWDk.js.map → p-KRGBGTu5.js.map} +0 -0
@@ -0,0 +1,197 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "empty-state",
4
+ "title": "EmptyState",
5
+ "description": "Centered empty-content message composed as icon plus title plus body, title plus body, or compact body-only content.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/empty-state",
11
+ "source": "src/wc/components/EmptyState/EmptyState.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "stable",
16
+ "summary": "Centered empty-content message composed as icon plus title plus body, title plus body, or compact body-only content.",
17
+ "useWhen": [
18
+ "A bounded content area is in a first-use, no-content, filtered no-results, or no-access state and needs a calm explanatory message tailored to that cause.",
19
+ "A prominent empty region benefits from a decorative icon, visual title, and supporting body copy.",
20
+ "A compact owner such as a Select popup needs only centered body copy."
21
+ ],
22
+ "avoidWhen": [
23
+ "Content is still loading; use Skeleton for predictable structure or Loader for an indeterminate operation.",
24
+ "The state is an error, warning, or confirmation that needs semantic intent and announcement from the owning workflow.",
25
+ "The state needs a recovery action, link, or other interactive content; the current EmptyState pattern is intentionally message-only.",
26
+ "A decorative illustration, arbitrary rich content, or custom layout is required."
27
+ ],
28
+ "alternatives": [
29
+ {
30
+ "when": "A content region is waiting for structured data.",
31
+ "component": "component:ds-skeleton",
32
+ "reason": "Skeleton preserves expected geometry while content is pending."
33
+ },
34
+ {
35
+ "when": "An operation is progressing without predictable content structure.",
36
+ "component": "component:ds-loader",
37
+ "reason": "Loader owns indeterminate progress rather than an empty outcome."
38
+ },
39
+ {
40
+ "when": "Only ordinary prose is needed without empty-state layout.",
41
+ "component": "component:ds-text",
42
+ "reason": "Text owns typography without adding centered empty-state composition."
43
+ }
44
+ ],
45
+ "commonlyComposedWith": [
46
+ "component:ds-select",
47
+ "component:ds-text",
48
+ "component:ds-icon"
49
+ ],
50
+ "accessibility": [
51
+ "The optional icon is decorative; the title and body provide all meaning in text.",
52
+ "EmptyState does not create a live region or alert automatically. The owner decides whether a dynamic transition requires announcement.",
53
+ "The visual title does not impose a document heading level because the correct hierarchy belongs to the consuming page or region.",
54
+ "Keep localized body copy concise and sufficient when using the body-only variant."
55
+ ],
56
+ "states": [
57
+ "Supported compositions are icon plus title plus body, title plus body, and body only; body copy is present in every supported composition.",
58
+ "The decorative icon renders at xl in the primary foreground with an 8px gap before the text group and only when a title is also provided.",
59
+ "The visual title uses title-small primary text; body uses body-medium secondary text with a 4px title-to-body gap.",
60
+ "All content is centered. The owner supplies container size and surrounding padding; do not compose actions into or immediately alongside the current EmptyState pattern.",
61
+ "First-use, no-content, no-results, and no-access states reuse this primitive with cause-specific localized messages rather than visual variants."
62
+ ],
63
+ "responsiveBehavior": [
64
+ "The owner controls available width and height; text wraps naturally while remaining centered.",
65
+ "EmptyState does not infer breakpoints or change composition automatically."
66
+ ],
67
+ "frameworkCaveats": {
68
+ "customElements": [
69
+ "Pass canonical IcoMo icon names and localized heading/body strings through properties or attributes."
70
+ ],
71
+ "react": [
72
+ "Choose one supported composition by passing body with optional heading and icon props."
73
+ ],
74
+ "angular": [
75
+ "Choose one supported composition by binding body with optional heading and icon inputs."
76
+ ]
77
+ },
78
+ "references": [
79
+ {
80
+ "label": "Storybook examples",
81
+ "path": "src/wc/components/EmptyState/EmptyState.stories.ts"
82
+ }
83
+ ]
84
+ },
85
+ "api": {
86
+ "props": {
87
+ "body": {
88
+ "type": "string",
89
+ "resolvedType": "string",
90
+ "attribute": "body",
91
+ "default": "''",
92
+ "required": false,
93
+ "mutable": false,
94
+ "description": "Explanatory empty-state copy. Every supported variant includes body text."
95
+ },
96
+ "heading": {
97
+ "type": "string | undefined",
98
+ "resolvedType": "string | undefined",
99
+ "attribute": "heading",
100
+ "required": false,
101
+ "mutable": false,
102
+ "description": "Optional visual title. Use with body; required when icon is provided."
103
+ },
104
+ "icon": {
105
+ "type": "string | undefined",
106
+ "resolvedType": "string | undefined",
107
+ "attribute": "icon",
108
+ "required": false,
109
+ "mutable": false,
110
+ "description": "Optional decorative prefix glyph for the complete icon + title + body variant."
111
+ }
112
+ },
113
+ "events": [],
114
+ "methods": [],
115
+ "slots": []
116
+ },
117
+ "props": {
118
+ "body": {
119
+ "type": "string",
120
+ "resolvedType": "string",
121
+ "attribute": "body",
122
+ "default": "''",
123
+ "required": false,
124
+ "mutable": false,
125
+ "description": "Explanatory empty-state copy. Every supported variant includes body text."
126
+ },
127
+ "heading": {
128
+ "type": "string | undefined",
129
+ "resolvedType": "string | undefined",
130
+ "attribute": "heading",
131
+ "required": false,
132
+ "mutable": false,
133
+ "description": "Optional visual title. Use with body; required when icon is provided."
134
+ },
135
+ "icon": {
136
+ "type": "string | undefined",
137
+ "resolvedType": "string | undefined",
138
+ "attribute": "icon",
139
+ "required": false,
140
+ "mutable": false,
141
+ "description": "Optional decorative prefix glyph for the complete icon + title + body variant."
142
+ }
143
+ },
144
+ "events": [],
145
+ "methods": [],
146
+ "slots": [],
147
+ "exports": {
148
+ "customElement": "ds-empty-state",
149
+ "react": "DsEmptyState",
150
+ "angular": "DsEmptyState"
151
+ },
152
+ "consumption": {
153
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
154
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
155
+ "customElements": {
156
+ "import": "import '@ds-mo/ui/dist/components/ds-empty-state.js';",
157
+ "example": "<ds-empty-state></ds-empty-state>"
158
+ },
159
+ "react": {
160
+ "import": "import { DsEmptyState } from '@ds-mo/ui/react';",
161
+ "example": "<DsEmptyState />"
162
+ },
163
+ "angular": {
164
+ "import": "import { DsEmptyState } from '@ds-mo/ui/angular/ds-empty-state';",
165
+ "example": "<ds-empty-state></ds-empty-state>"
166
+ },
167
+ "peerDependencies": {
168
+ "required": [
169
+ "@ds-mo/tokens ^3.1.1",
170
+ "@ds-mo/icons ^6.2.0"
171
+ ],
172
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
173
+ }
174
+ }
175
+ },
176
+ "dependencies": [
177
+ "@ds-mo/ui",
178
+ "@ds-mo/tokens",
179
+ "@ds-mo/icons"
180
+ ],
181
+ "registryDependencies": [
182
+ "icon",
183
+ "text"
184
+ ],
185
+ "files": [
186
+ {
187
+ "path": "src/wc/components/EmptyState/EmptyState.css",
188
+ "content": ":host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n:host,\n.empty-state,\n.empty-state__text {\n box-sizing: border-box;\n}\n\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--dimension-space-100);\n height: 100%;\n width: 100%;\n text-align: center;\n}\n\n.empty-state__icon {\n flex: 0 0 auto;\n}\n\n.empty-state__text {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--dimension-space-050);\n width: 100%;\n}\n",
189
+ "type": "registry:ui"
190
+ },
191
+ {
192
+ "path": "src/wc/components/EmptyState/EmptyState.tsx",
193
+ "content": "import { Component, Prop, h, Host } from '@stencil/core';\n\n@Component({\n tag: 'ds-empty-state',\n styleUrl: 'EmptyState.css',\n scoped: true,\n})\nexport class EmptyState {\n /** Optional decorative prefix glyph for the complete icon + title + body variant. */\n @Prop() icon: string | undefined;\n /** Optional visual title. Use with body; required when icon is provided. */\n @Prop() heading: string | undefined;\n /** Explanatory empty-state copy. Every supported variant includes body text. */\n @Prop() body: string = '';\n\n render() {\n const showHeading = Boolean(this.heading?.trim());\n const showIcon = showHeading && Boolean(this.icon);\n\n return (\n <Host>\n <div class=\"empty-state\">\n {showIcon && (\n <ds-icon\n class=\"empty-state__icon\"\n name={this.icon}\n size=\"xl\"\n color=\"primary\"\n />\n )}\n <div class=\"empty-state__text\">\n {showHeading && (\n <ds-text\n class=\"empty-state__title\"\n as=\"div\"\n variant=\"text-title-small\"\n color=\"primary\"\n align=\"center\"\n >\n {this.heading}\n </ds-text>\n )}\n {this.body && (\n <ds-text\n class=\"empty-state__body\"\n as=\"p\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n align=\"center\"\n >\n {this.body}\n </ds-text>\n )}\n </div>\n </div>\n </Host>\n );\n }\n}\n",
194
+ "type": "registry:ui"
195
+ }
196
+ ]
197
+ }
@@ -0,0 +1,222 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "field",
4
+ "title": "Field",
5
+ "description": "Accessible form-field composition that gives one Input or Select a persistent label, optional guidance, shared error presentation, stable IDs, and observable interaction state.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/field",
11
+ "source": "src/wc/components/Field/Field.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "stable",
16
+ "summary": "Accessible form-field composition that gives one Input or Select a persistent label, optional guidance, shared error presentation, stable IDs, and observable interaction state.",
17
+ "useWhen": [
18
+ "An Input, Select, or SelectMulti needs a persistent visible label in a form.",
19
+ "Help text or an error message must be programmatically associated with one control.",
20
+ "A form layout needs one consistent label-control-message rhythm."
21
+ ],
22
+ "avoidWhen": [
23
+ "The component already owns its visible label and complete selection semantics, such as Checkbox, Radio, or Switch.",
24
+ "Several controls belong to one question; use a semantic fieldset and legend instead.",
25
+ "Text describes a region or group rather than one form control."
26
+ ],
27
+ "alternatives": [
28
+ {
29
+ "when": "Several related controls answer one question.",
30
+ "component": "component:ds-text",
31
+ "reason": "Use native fieldset and legend semantics, then compose Text only for supporting content."
32
+ }
33
+ ],
34
+ "commonlyComposedWith": [
35
+ "component:ds-input",
36
+ "component:ds-select",
37
+ "component:ds-select-multi",
38
+ "component:ds-text"
39
+ ],
40
+ "accessibility": [
41
+ "Slot exactly one form control. Field assigns its stable control ID and connects the visible label automatically.",
42
+ "The visible description or error ID is merged with any authored aria-describedby references; a rendered error replaces the description.",
43
+ "The error prop forwards aria-invalid and supported design-system control error styling; put visible error text on Field, not again on the child control.",
44
+ "Clicking the label focuses the slotted design-system control without opening a Select popup.",
45
+ "Do not use placeholder text as the only label. Keep label text concise and put format or consequence guidance in description."
46
+ ],
47
+ "states": [
48
+ "Field exposes data-focused, data-filled, data-dirty, data-touched, data-invalid, data-disabled, and data-required hooks while the slotted control remains the source of value and interaction behavior.",
49
+ "A rendered error replaces the description; preserve essential guidance in the error message so users retain what they need to correct the field.",
50
+ "Field owns validation presentation but not validation timing; the form decides when to set error and errorMessage.",
51
+ "The label uses primary body-small emphasis, description uses secondary body-small text, and error uses negative body-small text."
52
+ ],
53
+ "responsiveBehavior": [
54
+ "Field fills the width supplied by its parent and makes the slotted control fill that field width.",
55
+ "The form layout owns column count, wrapping, and responsive placement; Field keeps the vertical label-control-message composition intact."
56
+ ],
57
+ "frameworkCaveats": {
58
+ "customElements": [
59
+ "Set fieldId only when another element must reference a predictable control ID; otherwise use the generated association.",
60
+ "Assign child component values and option arrays through their normal JavaScript properties."
61
+ ],
62
+ "react": [
63
+ "Compose one DsInput, DsSelect, or DsSelectMulti child and control validation timing in the form owner."
64
+ ],
65
+ "angular": [
66
+ "Compose the generated Field adapter around the control adapter and its matching Angular value accessor."
67
+ ]
68
+ },
69
+ "references": [
70
+ {
71
+ "label": "Storybook examples",
72
+ "path": "src/wc/components/Field/Field.stories.ts"
73
+ },
74
+ {
75
+ "label": "Cross-browser form and accessibility coverage",
76
+ "path": "tests/e2e/forms.spec.ts"
77
+ }
78
+ ]
79
+ },
80
+ "api": {
81
+ "props": {
82
+ "description": {
83
+ "type": "string | undefined",
84
+ "resolvedType": "string | undefined",
85
+ "attribute": "description",
86
+ "required": false,
87
+ "mutable": false,
88
+ "description": "Optional guidance associated with the slotted control while no visible error is shown."
89
+ },
90
+ "error": {
91
+ "type": "boolean",
92
+ "resolvedType": "boolean",
93
+ "attribute": "error",
94
+ "default": "false",
95
+ "required": false,
96
+ "mutable": false,
97
+ "description": "Invalid visual and accessible state forwarded to supported ds controls."
98
+ },
99
+ "errorMessage": {
100
+ "type": "string | undefined",
101
+ "resolvedType": "string | undefined",
102
+ "attribute": "error-message",
103
+ "required": false,
104
+ "mutable": false,
105
+ "description": "Visible error associated with the slotted control while error is true."
106
+ },
107
+ "fieldId": {
108
+ "type": "string | undefined",
109
+ "resolvedType": "string | undefined",
110
+ "attribute": "field-id",
111
+ "required": false,
112
+ "mutable": false,
113
+ "description": "Explicit ID for the slotted control; generated when omitted."
114
+ },
115
+ "label": {
116
+ "type": "string",
117
+ "resolvedType": "string",
118
+ "attribute": "label",
119
+ "required": true,
120
+ "mutable": false,
121
+ "description": "Persistent visible label for the single slotted control."
122
+ }
123
+ },
124
+ "events": [],
125
+ "methods": [],
126
+ "slots": []
127
+ },
128
+ "props": {
129
+ "description": {
130
+ "type": "string | undefined",
131
+ "resolvedType": "string | undefined",
132
+ "attribute": "description",
133
+ "required": false,
134
+ "mutable": false,
135
+ "description": "Optional guidance associated with the slotted control while no visible error is shown."
136
+ },
137
+ "error": {
138
+ "type": "boolean",
139
+ "resolvedType": "boolean",
140
+ "attribute": "error",
141
+ "default": "false",
142
+ "required": false,
143
+ "mutable": false,
144
+ "description": "Invalid visual and accessible state forwarded to supported ds controls."
145
+ },
146
+ "errorMessage": {
147
+ "type": "string | undefined",
148
+ "resolvedType": "string | undefined",
149
+ "attribute": "error-message",
150
+ "required": false,
151
+ "mutable": false,
152
+ "description": "Visible error associated with the slotted control while error is true."
153
+ },
154
+ "fieldId": {
155
+ "type": "string | undefined",
156
+ "resolvedType": "string | undefined",
157
+ "attribute": "field-id",
158
+ "required": false,
159
+ "mutable": false,
160
+ "description": "Explicit ID for the slotted control; generated when omitted."
161
+ },
162
+ "label": {
163
+ "type": "string",
164
+ "resolvedType": "string",
165
+ "attribute": "label",
166
+ "required": true,
167
+ "mutable": false,
168
+ "description": "Persistent visible label for the single slotted control."
169
+ }
170
+ },
171
+ "events": [],
172
+ "methods": [],
173
+ "slots": [],
174
+ "exports": {
175
+ "customElement": "ds-field",
176
+ "react": "DsField",
177
+ "angular": "DsField"
178
+ },
179
+ "consumption": {
180
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
181
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
182
+ "customElements": {
183
+ "import": "import '@ds-mo/ui/dist/components/ds-field.js';",
184
+ "example": "<ds-field></ds-field>"
185
+ },
186
+ "react": {
187
+ "import": "import { DsField } from '@ds-mo/ui/react';",
188
+ "example": "<DsField />"
189
+ },
190
+ "angular": {
191
+ "import": "import { DsField } from '@ds-mo/ui/angular/ds-field';",
192
+ "example": "<ds-field></ds-field>"
193
+ },
194
+ "peerDependencies": {
195
+ "required": [
196
+ "@ds-mo/tokens ^3.1.1",
197
+ "@ds-mo/icons ^6.2.0"
198
+ ],
199
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
200
+ }
201
+ }
202
+ },
203
+ "dependencies": [
204
+ "@ds-mo/ui",
205
+ "@ds-mo/tokens"
206
+ ],
207
+ "registryDependencies": [
208
+ "text"
209
+ ],
210
+ "files": [
211
+ {
212
+ "path": "src/wc/components/Field/Field.css",
213
+ "content": ":host {\n display: block;\n}\n\n.field {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n}\n\n.field__control {\n display: flex;\n min-width: 0;\n}\n\n.field__control > * {\n width: 100%;\n}\n",
214
+ "type": "registry:ui"
215
+ },
216
+ {
217
+ "path": "src/wc/components/Field/Field.tsx",
218
+ "content": "import { Component, Element, Prop, State, Watch, h, Host } from '@stencil/core';\n\ninterface FieldControl extends HTMLElement {\n value?: string | string[];\n inputId?: string;\n error?: boolean;\n disabled?: boolean;\n isInactive?: boolean;\n required?: boolean;\n setFocus?: () => Promise<void> | void;\n}\n\ninterface AuthoredAria {\n labelledby: string[];\n describedby: string[];\n}\n\nlet fieldCounter = 0;\n\nconst tokens = (value: string | null | undefined): string[] =>\n value?.split(/\\s+/).filter(Boolean) ?? [];\n\nconst uniqueTokens = (...groups: string[][]): string[] => [...new Set(groups.flat())];\n\n@Component({\n tag: 'ds-field',\n styleUrl: 'Field.css',\n scoped: true,\n})\nexport class Field {\n @Element() el!: HTMLElement;\n\n /** Persistent visible label for the single slotted control. */\n @Prop() label!: string;\n /** Explicit ID for the slotted control; generated when omitted. */\n @Prop() fieldId: string | undefined;\n /** Optional guidance associated with the slotted control while no visible error is shown. */\n @Prop() description: string | undefined;\n /** Invalid visual and accessible state forwarded to supported ds controls. */\n @Prop() error: boolean = false;\n /** Visible error associated with the slotted control while error is true. */\n @Prop() errorMessage: string | undefined;\n\n @State() private focused = false;\n @State() private filled = false;\n @State() private dirty = false;\n @State() private touched = false;\n @State() private controlDisabled = false;\n @State() private controlRequired = false;\n\n private readonly generatedId = `ds-field-${++fieldCounter}`;\n private controlContainer?: HTMLDivElement;\n private control?: FieldControl;\n private initialValue = '';\n private authoredAria = new WeakMap<FieldControl, AuthoredAria>();\n\n componentDidLoad() {\n this.syncControl();\n requestAnimationFrame(() => this.syncControl());\n }\n\n componentDidRender() {\n this.syncControl();\n }\n\n @Watch('fieldId')\n @Watch('description')\n @Watch('error')\n @Watch('errorMessage')\n onFieldContractChange() {\n this.syncControl();\n }\n\n private get controlId(): string {\n return this.fieldId || this.generatedId;\n }\n\n private get labelId(): string {\n return `${this.controlId}-label`;\n }\n\n private get descriptionId(): string {\n return `${this.controlId}-description`;\n }\n\n private get errorId(): string {\n return `${this.controlId}-error`;\n }\n\n private get renderedDescription(): boolean {\n return Boolean(this.description) && !this.renderedError;\n }\n\n private get renderedError(): boolean {\n return this.error && Boolean(this.errorMessage);\n }\n\n private readValue(control: FieldControl): string {\n const value = control.value;\n return Array.isArray(value) ? value.join('\\u001f') : String(value ?? '');\n }\n\n private updateControlAttribute(control: FieldControl, name: string, value: string | undefined) {\n if (value === undefined) {\n if (control.hasAttribute(name)) control.removeAttribute(name);\n return;\n }\n if (control.getAttribute(name) !== value) control.setAttribute(name, value);\n }\n\n private findControl(): FieldControl | undefined {\n return this.controlContainer?.querySelector<FieldControl>(':scope > :not(slot)') ?? undefined;\n }\n\n private syncControl = () => {\n const control = this.findControl();\n if (!control) return;\n\n if (control !== this.control) {\n this.control = control;\n this.initialValue = this.readValue(control);\n this.authoredAria.set(control, {\n labelledby: tokens(control.getAttribute('aria-labelledby')),\n describedby: tokens(control.getAttribute('aria-describedby')),\n });\n }\n\n const isDsControl = control.tagName.startsWith('DS-');\n if (isDsControl && 'inputId' in control) control.inputId = this.controlId;\n else this.updateControlAttribute(control, 'id', this.controlId);\n\n const authored = this.authoredAria.get(control) ?? { labelledby: [], describedby: [] };\n const labelledby = uniqueTokens(authored.labelledby, [this.labelId]).join(' ');\n const describedby = uniqueTokens(\n authored.describedby,\n this.renderedDescription ? [this.descriptionId] : [],\n this.renderedError ? [this.errorId] : [],\n ).join(' ');\n\n this.updateControlAttribute(control, 'aria-labelledby', labelledby);\n this.updateControlAttribute(control, 'aria-describedby', describedby || undefined);\n this.updateControlAttribute(control, 'aria-invalid', this.error ? 'true' : undefined);\n\n if (isDsControl && 'error' in control && control.error !== this.error) control.error = this.error;\n\n const value = this.readValue(control);\n this.filled = value.length > 0;\n this.dirty = value !== this.initialValue;\n this.controlDisabled = Boolean(control.disabled || control.isInactive || control.hasAttribute('disabled'));\n this.controlRequired = Boolean(control.required || control.hasAttribute('required'));\n };\n\n private handleLabelClick = (event: MouseEvent) => {\n if (!this.control?.tagName.startsWith('DS-')) return;\n event.preventDefault();\n this.control.setFocus?.();\n };\n\n private handleFocusIn = () => {\n this.focused = true;\n };\n\n private handleFocusOut = (event: FocusEvent) => {\n if (event.relatedTarget instanceof Node && this.el.contains(event.relatedTarget)) return;\n this.focused = false;\n this.touched = true;\n };\n\n private handleValueChange = () => {\n this.syncControl();\n };\n\n render() {\n return (\n <Host\n data-focused={this.focused ? '' : undefined}\n data-filled={this.filled ? '' : undefined}\n data-dirty={this.dirty ? '' : undefined}\n data-touched={this.touched ? '' : undefined}\n data-invalid={this.error ? '' : undefined}\n data-disabled={this.controlDisabled ? '' : undefined}\n data-required={this.controlRequired ? '' : undefined}\n onFocusin={this.handleFocusIn}\n onFocusout={this.handleFocusOut}\n onInput={this.handleValueChange}\n onChange={this.handleValueChange}\n onDsChange={this.handleValueChange}\n >\n <div class=\"field\">\n <ds-text\n class=\"field__label\"\n as=\"label\"\n variant=\"text-body-small\"\n color=\"primary\"\n emphasis\n for={this.controlId}\n textId={this.labelId}\n onClick={this.handleLabelClick}\n >\n {this.label}\n </ds-text>\n <div class=\"field__control\" ref={el => (this.controlContainer = el)}>\n <slot onSlotchange={this.syncControl} />\n </div>\n {this.renderedDescription && (\n <ds-text\n class=\"field__description\"\n as=\"div\"\n variant=\"text-body-small\"\n color=\"secondary\"\n textId={this.descriptionId}\n >\n {this.description}\n </ds-text>\n )}\n {this.renderedError && (\n <ds-text\n class=\"field__error\"\n as=\"div\"\n variant=\"text-body-small\"\n color=\"negative\"\n textId={this.errorId}\n role=\"alert\"\n >\n {this.errorMessage}\n </ds-text>\n )}\n </div>\n </Host>\n );\n }\n}\n",
219
+ "type": "registry:ui"
220
+ }
221
+ ]
222
+ }