@dataworkz/crystal 0.1.0-beta.0 → 0.1.0-beta.10

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 (43) hide show
  1. package/CHANGELOG.md +151 -13
  2. package/README.md +6 -0
  3. package/fesm2022/dataworkz-crystal-icons.mjs +32 -322
  4. package/fesm2022/dataworkz-crystal-icons.mjs.map +1 -1
  5. package/fesm2022/dataworkz-crystal-markdown.mjs +1 -1
  6. package/fesm2022/dataworkz-crystal-markdown.mjs.map +1 -1
  7. package/fesm2022/dataworkz-crystal-primeng.mjs +31 -5
  8. package/fesm2022/dataworkz-crystal-primeng.mjs.map +1 -1
  9. package/fesm2022/dataworkz-crystal.mjs +2635 -257
  10. package/fesm2022/dataworkz-crystal.mjs.map +1 -1
  11. package/icons/index.d.ts +33 -97
  12. package/icons/svg/files/csv.svg +0 -1
  13. package/icons/svg/files/generic.svg +0 -1
  14. package/icons/svg/files/multimedia.svg +0 -1
  15. package/icons/svg/glyphs/agentBuilder.svg +1 -1
  16. package/icons/svg/glyphs/notesPad.svg +1 -1
  17. package/icons/svg/glyphs/panelCollapse.svg +1 -1
  18. package/icons/svg/glyphs/panelExpand.svg +1 -1
  19. package/icons/svg/glyphs/play.svg +1 -1
  20. package/icons/svg/glyphs/robot.svg +1 -1
  21. package/icons/svg/glyphs/sidebarCollapse.svg +1 -1
  22. package/icons/svg/glyphs/sidebarExpand.svg +1 -1
  23. package/icons/svg/glyphs/tagFilled.svg +1 -1
  24. package/icons/svg/glyphs/testShield.svg +1 -1
  25. package/icons/svg/storage/generic.svg +0 -1
  26. package/icons/svg/storage/legacy/microsoft-mail.svg +0 -1
  27. package/icons/svg/storage/local/microsoft-mail.svg +0 -1
  28. package/index.d.ts +1207 -68
  29. package/package.json +5 -7
  30. package/primeng/index.d.ts +45 -5
  31. package/styles/base.css +4 -4
  32. package/styles/components/button.css +3 -0
  33. package/styles/components/content.css +89 -91
  34. package/styles/components/data.css +24 -1
  35. package/styles/components/form.css +116 -13
  36. package/styles/components/navigation.css +31 -1
  37. package/styles/components/overlay.css +16 -1
  38. package/styles/components/surface.css +38 -2
  39. package/styles/components/toast.css +1 -1
  40. package/styles/crystal-no-fonts.css +3 -2
  41. package/styles/icons.css +6 -0
  42. package/styles/tokens/primitives.css +1 -3
  43. package/styles/tokens/semantic-dark.css +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataworkz/crystal",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.10",
4
4
  "description": "Dataworkz core UI for Angular: dw-* components, icons, toasts, design tokens, theme CSS and bundled fonts.",
5
5
  "license": "MIT",
6
6
  "author": "Dataworkz",
@@ -19,21 +19,19 @@
19
19
  "@angular/forms": "^20.3.0",
20
20
  "@angular/platform-browser": "^20.3.0",
21
21
  "@angular/router": "^20.3.0",
22
- "@primeng/themes": "^21.0.0",
23
22
  "ngx-markdown": "^20.0.0",
24
- "primeicons": "^7.0.0",
25
- "primeng": "^20.4.0",
26
23
  "rxjs": "~7.8.0"
27
24
  },
28
25
  "peerDependenciesMeta": {
29
- "@primeng/themes": {
30
- "optional": true
31
- },
32
26
  "ngx-markdown": {
33
27
  "optional": true
34
28
  }
35
29
  },
36
30
  "dependencies": {
31
+ "@primeng/themes": "^21.0.0",
32
+ "chart.js": "^4.5.1",
33
+ "primeicons": "^7.0.0",
34
+ "primeng": "~20.4.0",
37
35
  "tslib": "^2.8.1"
38
36
  },
39
37
  "exports": {
@@ -5,7 +5,7 @@ import { PrimeNGConfigType } from 'primeng/config';
5
5
 
6
6
  /**
7
7
  * Azure Electric primary ramp. Same hex values as `--dw-primary-*` in
8
- * src/styles/tokens/primitives.css — PrimeNG needs literal hex for the
8
+ * tokens/primitives.css — PrimeNG needs literal hex for the
9
9
  * primitive ramp because it derives other tokens from it at build time.
10
10
  */
11
11
  declare const AZURE_PRIMARY_RAMP: {
@@ -23,8 +23,8 @@ declare const AZURE_PRIMARY_RAMP: {
23
23
  };
24
24
  /**
25
25
  * PrimeNG color scheme, used for both light and dark. Every value points at a
26
- * design-system CSS variable, so PrimeNG follows src/styles/tokens/semantic.css
27
- * and semantic-dark.css: switching theme re-points the variables, the preset
26
+ * design-system CSS variable, so PrimeNG follows the semantic tokens (tokens/semantic.css
27
+ * and semantic-dark.css): switching theme re-points the variables, the preset
28
28
  * never changes. (The surface ramp is the raw neutral scale, light → dark,
29
29
  * which is the order Aura's own light and dark schemes expect.)
30
30
  */
@@ -608,8 +608,48 @@ declare const DW_PRESET_OVERRIDES: {
608
608
  /** Dataworkz PrimeNG preset: Aura retargeted to the design system. */
609
609
  declare const DwPreset: _primeuix_themes_types.Preset<_primeuix_themes_aura_base.AuraBaseDesignTokens>;
610
610
 
611
- /** PrimeNG's dark mode follows the attribute ThemeService (and provideCrystal()) put on <html>. */
611
+ /** PrimeNG's dark mode follows the attribute ThemeService (and provideCrystal()) put on `<html>`. */
612
612
  declare const DW_DARK_MODE_SELECTOR = "[data-theme=\"dark\"]";
613
+ /** Panel class that sizes a select's option list for a small trigger (components/form.css). */
614
+ declare const DW_SELECT_PANEL_SM = "dw-select-panel--sm";
615
+ type SizedInstance = {
616
+ size?: () => string | null | undefined;
617
+ };
618
+ /**
619
+ * Crystal's pass-through defaults: a small `p-select` / `p-multiselect` gets small options even when its list is
620
+ * appended to `<body>` (where CSS can't see the trigger).
621
+ */
622
+ declare const DW_PRIMENG_PT: {
623
+ readonly select: {
624
+ readonly overlay: ({ instance }: {
625
+ instance?: SizedInstance;
626
+ }) => {
627
+ class: string;
628
+ } | {
629
+ class?: undefined;
630
+ };
631
+ };
632
+ readonly multiselect: {
633
+ readonly overlay: ({ instance }: {
634
+ instance?: SizedInstance;
635
+ }) => {
636
+ class: string;
637
+ } | {
638
+ class?: undefined;
639
+ };
640
+ };
641
+ };
642
+ /**
643
+ * PrimeNG's z-index bases, with tooltips raised to 5000: PrimeNG already stacks a tooltip above the overlays it manages
644
+ * (dialogs, drawers, popovers), and the higher base keeps it above layers that set their own z-index too (a panel or
645
+ * page chrome with `z-index` in CSS), so a tooltip is never hidden under what it describes.
646
+ */
647
+ declare const DW_PRIMENG_Z_INDEX: {
648
+ readonly modal: 1100;
649
+ readonly overlay: 1000;
650
+ readonly menu: 1000;
651
+ readonly tooltip: 5000;
652
+ };
613
653
  /**
614
654
  * PrimeNG with the Dataworkz preset, dark mode on `[data-theme="dark"]`. Pass app-specific PrimeNG settings
615
655
  * (ripple, zIndex, translation…) as `config`; its `theme.options` are merged over crystal's.
@@ -620,4 +660,4 @@ declare const DW_DARK_MODE_SELECTOR = "[data-theme=\"dark\"]";
620
660
  */
621
661
  declare function provideCrystalPrimeNG(config?: PrimeNGConfigType): EnvironmentProviders;
622
662
 
623
- export { AZURE_PRIMARY_RAMP, DW_COLOR_SCHEME, DW_DARK_MODE_SELECTOR, DW_PRESET_OVERRIDES, DwPreset, provideCrystalPrimeNG };
663
+ export { AZURE_PRIMARY_RAMP, DW_COLOR_SCHEME, DW_DARK_MODE_SELECTOR, DW_PRESET_OVERRIDES, DW_PRIMENG_PT, DW_PRIMENG_Z_INDEX, DW_SELECT_PANEL_SM, DwPreset, provideCrystalPrimeNG };
package/styles/base.css CHANGED
@@ -1,12 +1,12 @@
1
1
  /* ============================================================================
2
2
  Dataworkz Design System — BASE
3
- The ONLY place in the app that declares font-family (element-base.css is
4
- its equivalent for the federated elements on the legacy page). Everything else
3
+ The ONLY place in the theme that declares font-family (element-base.css is
4
+ its equivalent for components embedded in a page crystal doesn't own). Everything else
5
5
  inherits (`font: inherit`) or uses the typography classes.
6
6
  ============================================================================ */
7
7
 
8
- /* ── Reset — the element defaults the app was built on (ported from the
9
- Bootstrap 4 Reboot when Bootstrap was removed) ── */
8
+ /* ── Reset — the element defaults the components are built on (in the spirit of the
9
+ Bootstrap 4 Reboot) ── */
10
10
  *,
11
11
  *::before,
12
12
  *::after {
@@ -325,6 +325,9 @@
325
325
  .dw-icon-btn.is-active { background: var(--dw-accent-tint); color: var(--dw-accent); }
326
326
  /* Hovering the active (selected) button keeps it accented, a step stronger */
327
327
  .dw-icon-btn.is-active:hover:not(:disabled) { background: var(--dw-accent-tint-strong); color: var(--dw-accent); }
328
+ /* A selected danger toggle (e.g. a "not helpful" rating) keeps its danger colour instead of the accent. */
329
+ .dw-icon-btn--danger.is-active,
330
+ .dw-icon-btn--danger.is-active:hover:not(:disabled) { background: var(--dw-danger-tint); color: var(--dw-danger-solid); }
328
331
  .dw-icon-btn--inverse { color: var(--dw-text-muted); }
329
332
  .dw-icon-btn--inverse:hover:not(:disabled),
330
333
  .dw-icon-btn--inverse.is-active { background: color-mix(in srgb, var(--dw-text-inverse) 12%, transparent); color: var(--dw-text-inverse); }
@@ -153,108 +153,106 @@ dw-markdown.dw-prose { display: block; }
153
153
  .dw-prose--compact { font-size: var(--dw-font-size-12); line-height: 1.5; }
154
154
  .dw-prose--compact p { margin-bottom: var(--dw-space-1); }
155
155
 
156
- /* ── Citation chip — numbered source reference in rendered answers (RAG run,
157
- Q&A history, probe). The markup is generated by the RAG answer services. ── */
158
- .dw-rag-cite-chip {
159
- display: inline-flex;
160
- align-items: center;
161
- justify-content: center;
162
- min-width: 22px;
163
- height: 22px;
164
- padding: 0 6px;
165
- margin: 0 2px;
166
- border-radius: var(--dw-radius-sm);
167
- border: 1px solid var(--dw-border-accent);
168
- background: var(--dw-accent-tint);
169
- color: var(--dw-accent-text);
170
- font-size: var(--dw-font-size-12);
171
- font-weight: var(--dw-font-weight-semibold);
172
- line-height: var(--dw-line-height-20);
173
- text-decoration: none;
174
- vertical-align: middle;
175
- }
176
-
177
- a.dw-rag-cite-chip:hover {
178
- background: var(--dw-accent-tint-strong);
179
- text-decoration: none;
180
- }
181
-
182
156
  /* ── dw-icon: app SVG glyphs, drawn in currentColor at the text size ── */
183
157
  .dw-icon { display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; line-height: 0; vertical-align: -0.125em; }
184
158
  .dw-icon svg { display: block; }
159
+ /* A PrimeIcons glyph (dw-icon pi="…") is a font glyph: it needs a line box, which the SVG case sets to 0. */
160
+ .dw-icon.pi { line-height: 1; }
161
+ .dw-icon--spin { animation: dw-icon-spin 1s linear infinite; }
162
+ @keyframes dw-icon-spin { to { transform: rotate(360deg); } }
163
+ @media (prefers-reduced-motion: reduce) { .dw-icon--spin, .dw-icon.pi-spin { animation-duration: 3s; } }
185
164
 
186
- /* ── Chat message: avatar · author line · bubble ──
187
- <div class="dw-msg dw-msg--user"><span class="dw-msg__avatar"><i class="pi pi-user"></i></span>
188
- <div class="dw-msg__main"><div class="dw-msg__head"><span class="dw-msg__author">Ana</span>
189
- <span class="dw-msg__role">User query</span></div><div class="dw-msg__bubble">…</div></div></div> */
190
- .dw-msg { display: flex; gap: 10px; }
191
- .dw-msg + .dw-msg { margin-top: 14px; }
192
- .dw-msg__avatar {
193
- display: inline-flex;
194
- flex-shrink: 0;
195
- align-items: center;
196
- justify-content: center;
197
- width: 30px;
198
- height: 30px;
199
- margin-top: 2px;
200
- border: 1px solid var(--dw-border);
201
- border-radius: var(--dw-radius-full);
202
- background: var(--dw-surface-pressed);
203
- color: var(--dw-text-secondary);
204
- font-size: var(--dw-font-size-12);
165
+ /* ── dw-accordion: panels that open and close under a header row (PrimeNG's p-accordion underneath) ──
166
+ PrimeNG reads its accordion design tokens as --p-accordion-* variables, so the look is set through them (no
167
+ specificity race with its own :hover / :first-child rules). flush (default): rows on the container, a divider
168
+ between panels; card: each panel a bordered card, the list spaced, a divider under an open header. */
169
+ .dw-accordion {
170
+ display: block;
171
+ min-width: 0;
172
+ --p-accordion-panel-border-width: 0;
173
+ --p-accordion-panel-border-color: var(--dw-border);
174
+ --p-accordion-header-padding: var(--dw-space-3) var(--dw-space-4);
175
+ --p-accordion-header-font-weight: var(--dw-font-weight-semibold);
176
+ --p-accordion-header-color: var(--dw-text-strong);
177
+ --p-accordion-header-hover-color: var(--dw-text-strong);
178
+ --p-accordion-header-active-color: var(--dw-text-strong);
179
+ --p-accordion-header-active-hover-color: var(--dw-text-strong);
180
+ --p-accordion-header-background: transparent;
181
+ --p-accordion-header-hover-background: var(--dw-surface-hover);
182
+ --p-accordion-header-active-background: transparent;
183
+ --p-accordion-header-active-hover-background: var(--dw-surface-hover);
184
+ --p-accordion-header-border-width: 0;
185
+ --p-accordion-header-border-color: var(--dw-border);
186
+ --p-accordion-header-border-radius: 0;
187
+ --p-accordion-header-first-border-width: 0;
188
+ --p-accordion-header-first-top-border-radius: 0;
189
+ --p-accordion-header-last-bottom-border-radius: 0;
190
+ --p-accordion-header-last-active-bottom-border-radius: 0;
191
+ --p-accordion-header-focus-ring-width: 0;
192
+ --p-accordion-header-focus-ring-shadow: inset var(--dw-focus-ring);
193
+ --p-accordion-content-background: transparent;
194
+ --p-accordion-content-color: var(--dw-text);
195
+ --p-accordion-content-padding: 0 var(--dw-space-4) var(--dw-space-4);
205
196
  }
206
- .dw-msg__main { flex: 1; min-width: 0; }
207
- .dw-msg__head { display: flex; align-items: center; gap: 6px; margin-bottom: var(--dw-space-1); }
208
- .dw-msg__author { color: var(--dw-text); font-size: var(--dw-font-size-12); font-weight: var(--dw-font-weight-semibold); }
209
- .dw-msg__role { color: var(--dw-text-secondary); font-size: var(--dw-font-size-12); }
210
- .dw-msg__bubble {
211
- padding: 10px 14px;
212
- border: 1px solid var(--dw-border);
213
- border-radius: var(--dw-radius-md) var(--dw-radius-md) var(--dw-radius-md) 2px;
214
- background: var(--dw-surface-pressed);
215
- color: var(--dw-text);
197
+
198
+ .dw-accordion .p-accordionheader {
199
+ gap: var(--dw-space-3);
216
200
  font-size: var(--dw-font-size-14);
217
- line-height: var(--dw-line-height-20);
218
- white-space: pre-wrap;
219
- overflow-wrap: anywhere;
201
+ line-height: 1.4;
220
202
  }
221
- .dw-msg--user .dw-msg__avatar { border-color: var(--dw-border-accent); background: var(--dw-accent-tint); color: var(--dw-accent); }
222
- .dw-msg--user .dw-msg__bubble { border-color: var(--dw-border-accent); background: var(--dw-accent-tint); color: var(--dw-accent-text); }
223
- .dw-msg--agent .dw-msg__avatar { border-color: var(--dw-status-success-border); background: var(--dw-status-success-bg); color: var(--dw-status-success-fg); }
224
-
225
- /* ── Chat composer: a borderless textarea + toolbar inside one card ──
226
- <div class="dw-composer"><textarea class="dw-composer__input" …></textarea>
227
- <div class="dw-composer__toolbar"><dw-button …/><span class="dw-composer__spacer"></span>
228
- <span class="dw-composer__hint">⌘ Enter</span><dw-button label="Send" …/></div></div> */
229
- .dw-composer {
203
+
204
+ .dw-accordion__header {
230
205
  display: flex;
231
- flex-direction: column;
206
+ flex: 1 1 auto;
207
+ align-items: center;
232
208
  gap: var(--dw-space-2);
233
- padding: var(--dw-space-3) var(--dw-space-3) var(--dw-space-2);
209
+ min-width: 0;
210
+ }
211
+
212
+ .dw-accordion__title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
213
+
214
+ .dw-accordion__chevron {
215
+ flex: none;
216
+ font-size: var(--dw-font-size-12);
217
+ color: var(--dw-text-secondary);
218
+ transition: transform var(--dw-dur-enter) var(--dw-ease);
219
+ }
220
+
221
+ .dw-accordion .p-accordionpanel-active > .p-accordionheader .dw-accordion__chevron { transform: rotate(180deg); }
222
+ .dw-accordion .p-accordionpanel.p-disabled > .p-accordionheader { cursor: default; }
223
+
224
+ /* flush: a divider between panels */
225
+ .dw-accordion--flush .p-accordionpanel + .p-accordionpanel { --p-accordion-panel-border-width: 1px 0 0; }
226
+
227
+ /* card: each panel its own card; a closed one lifts on hover, an open one divides its header from its content */
228
+ .dw-accordion--card {
229
+ --p-accordion-header-hover-background: transparent;
230
+ --p-accordion-header-active-hover-background: transparent;
231
+ --p-accordion-header-border-radius: calc(var(--dw-radius-card) - 1px);
232
+ --p-accordion-header-first-top-border-radius: calc(var(--dw-radius-card) - 1px);
233
+ --p-accordion-header-last-bottom-border-radius: calc(var(--dw-radius-card) - 1px);
234
+ --p-accordion-content-padding: var(--dw-space-4);
235
+ }
236
+
237
+ .dw-accordion--card > .p-accordion { display: flex; flex-direction: column; gap: var(--dw-space-2); }
238
+
239
+ .dw-accordion--card .p-accordionpanel {
234
240
  border: 1px solid var(--dw-border);
235
- border-radius: var(--dw-radius-xl);
241
+ border-radius: var(--dw-radius-card);
236
242
  background: var(--dw-surface);
237
- box-shadow: var(--dw-shadow-sm);
238
- transition: border-color var(--dw-dur-micro) var(--dw-ease), box-shadow var(--dw-dur-micro) var(--dw-ease);
243
+ transition: box-shadow var(--dw-dur-enter) var(--dw-ease);
239
244
  }
240
- .dw-composer:focus-within { border-color: var(--dw-border-accent); box-shadow: var(--dw-focus-ring); }
241
- .dw-composer__input {
242
- width: 100%;
243
- min-height: 84px;
244
- max-height: 220px;
245
- padding: 0;
246
- border: 0;
247
- outline: none;
248
- resize: none;
249
- background: transparent;
250
- color: var(--dw-text);
251
- font-size: var(--dw-font-size-14);
252
- line-height: var(--dw-line-height-20);
245
+
246
+ .dw-accordion--card .p-accordionpanel:not(.p-accordionpanel-active):not(.p-disabled):hover { box-shadow: var(--dw-shadow-md); }
247
+
248
+ .dw-accordion--card .p-accordionpanel-active {
249
+ --p-accordion-header-border-width: 0 0 1px;
250
+ --p-accordion-header-first-border-width: 0 0 1px;
251
+ --p-accordion-header-border-radius: calc(var(--dw-radius-card) - 1px) calc(var(--dw-radius-card) - 1px) 0 0;
252
+ --p-accordion-header-last-active-bottom-border-radius: 0;
253
253
  }
254
- .dw-composer__input::placeholder { color: var(--dw-text-muted); }
255
- .dw-composer__toolbar { display: flex; align-items: center; gap: var(--dw-space-2); }
256
- .dw-composer__spacer { flex: 1; }
257
- .dw-composer__hint {
258
- color: var(--dw-text-muted);
259
- font-size: var(--dw-font-size-12);
254
+
255
+ @media (prefers-reduced-motion: reduce) {
256
+ .dw-accordion__chevron,
257
+ .dw-accordion--card .p-accordionpanel { transition: none; }
260
258
  }
@@ -79,7 +79,7 @@
79
79
  overflow-wrap: anywhere;
80
80
  word-break: break-word;
81
81
  }
82
- /* Dense detail panels (e.g. the Probe): 13px values; key column and spacing unchanged. */
82
+ /* Dense detail panels: 13px values; key column and spacing unchanged. */
83
83
  .dw-kv-list--sm .dw-kv-list__value { font-size: var(--dw-font-size-13); }
84
84
  .dw-kv-list__action { flex-shrink: 0; opacity: 0; transition: opacity var(--dw-dur-micro) var(--dw-ease); }
85
85
  .dw-kv-list__row:hover .dw-kv-list__action,
@@ -138,6 +138,29 @@ body .p-treetable .p-treetable-tbody > tr.p-treetable-row-selected {
138
138
  border-left: 1px solid var(--dw-tree-spine);
139
139
  }
140
140
 
141
+ /* dw-tree: no box of its own (it sits on its container); --framed draws one. Node content fills the row, so a
142
+ dwTreeNode template can lay out across it. */
143
+ .dw-tree-view { display: block; min-width: 0; }
144
+ .dw-tree-view > .p-tree { padding: 0; border: 0; border-radius: 0; background: transparent; }
145
+ .dw-tree-view.dw-tree-view--framed > .p-tree {
146
+ padding: var(--dw-space-2);
147
+ border: 1px solid var(--dw-border-strong);
148
+ border-radius: var(--dw-radius-control);
149
+ background: var(--dw-surface);
150
+ }
151
+ .dw-tree-view .p-tree-node-label { flex: 1 1 auto; min-width: 0; }
152
+ /* --sm: compact rows, through PrimeNG's own tree variables (row ~28px instead of ~36px). */
153
+ .dw-tree-view.dw-tree-view--sm {
154
+ --p-tree-node-padding: var(--dw-space-1) var(--dw-space-2);
155
+ --p-tree-node-gap: var(--dw-space-1);
156
+ --p-tree-node-toggle-button-size: var(--dw-space-5);
157
+ --p-tree-gap: 0;
158
+ font-size: var(--dw-font-size-13);
159
+ }
160
+ /* --marker: the selected row's accent bar, as dw-nav-list [marker] (an inset shadow, so nothing shifts). Specific
161
+ enough to outlast PrimeNG's focus styles, which it loads after crystal. */
162
+ .dw-tree-view.dw-tree-view--marker .p-tree-node-content.p-tree-node-selected { box-shadow: inset 3px 0 0 var(--dw-accent); }
163
+
141
164
  /* ── Skeleton ── */
142
165
  .dw-skeleton {
143
166
  height: 20px;
@@ -225,14 +225,15 @@ dw-field.dw-field--inline:has(.dw-field__control :is(.p-inputtext-sm, .p-textare
225
225
  .dw-field__control > :first-child:not([dwFieldSuffix]) { flex: 1 1 auto; min-width: 0; }
226
226
  /* Switches, checkboxes and radios keep their own size — stretching one turns a toggle into a bar. */
227
227
  /* (Same specificity as the stretch rule above, plus the element, so it wins.) */
228
- .dw-field__control > :is(p-toggleswitch, p-checkbox, p-radiobutton):first-child:not([dwFieldSuffix]) { flex: none; }
228
+ .dw-field__control > :is(p-toggleswitch, p-checkbox, p-radiobutton, dw-switch, dw-checkbox, dw-radio):first-child:not([dwFieldSuffix]) { flex: none; }
229
229
  .dw-field__control > input,
230
230
  .dw-field__control > textarea,
231
231
  .dw-field__control > p-select,
232
232
  .dw-field__control > p-multiselect,
233
233
  .dw-field__control > p-inputnumber,
234
234
  .dw-field__control > p-datepicker,
235
- .dw-field__control > p-autocomplete { width: 100%; }
235
+ .dw-field__control > p-autocomplete,
236
+ .dw-field__control > :is(dw-select, dw-multiselect, dw-number-input, dw-date-picker, dw-autocomplete, dw-slider) { width: 100%; }
236
237
  .dw-field__error { display: inline-flex; align-items: center; gap: var(--dw-space-1); }
237
238
  /* Field errors are text only (the red text and border carry the state): no leading icon, even in
238
239
  hand-written error lines. */
@@ -391,12 +392,6 @@ dw-search-input.dw-search--block { width: 100%; }
391
392
  display: none;
392
393
  }
393
394
 
394
- /* PrimeNG's slider host is inline; give it the field's width. */
395
- p-slider {
396
- display: block;
397
- width: 100%;
398
- }
399
-
400
395
  /* pSize="small" inputs: base.css's `font: inherit` would keep them at 14px, so restate the
401
396
  small type here — 5px padding + 16px line + 1px borders = 28px, the small dw-button height. */
402
397
  /* Small selects match small inputs exactly (28px): their label gets the same 16px line
@@ -470,12 +465,14 @@ p-slider {
470
465
 
471
466
 
472
467
  /* Small selects (size="small"): their option list matches the 12px trigger instead of the default
473
- 14px rows. The list renders inside the select unless it is appended elsewhere (appendTo); a list
474
- appended to <body> takes the panel class `dw-select-panel--sm` for the same sizing. */
468
+ 14px rows. A list rendered inside the select matches `.p-select-sm`; a list appended elsewhere
469
+ (appendTo="body") gets `dw-select-panel--sm` from provideCrystalPrimeNG()'s pass-through, so no
470
+ screen sets it by hand. */
475
471
  .p-select-sm .p-select-option,
476
472
  .p-multiselect-sm .p-multiselect-option,
477
473
  .dw-select-panel--sm .p-select-option,
478
- .dw-select-panel--sm .p-multiselect-option {
474
+ .dw-select-panel--sm .p-multiselect-option,
475
+ .dw-autocomplete-panel--sm .p-autocomplete-option {
479
476
  padding: 5px 10px;
480
477
  font-size: var(--dw-font-size-12);
481
478
  line-height: var(--dw-line-height-16);
@@ -483,7 +480,8 @@ p-slider {
483
480
  .p-select-sm .p-select-empty-message,
484
481
  .p-multiselect-sm .p-multiselect-empty-message,
485
482
  .dw-select-panel--sm .p-select-empty-message,
486
- .dw-select-panel--sm .p-multiselect-empty-message {
483
+ .dw-select-panel--sm .p-multiselect-empty-message,
484
+ .dw-autocomplete-panel--sm .p-autocomplete-empty-message {
487
485
  padding: 5px 10px;
488
486
  font-size: var(--dw-font-size-12);
489
487
  }
@@ -635,7 +633,7 @@ body .dw-var-field .p-textarea {
635
633
  .dw-check-toggle.is-on .dw-check-toggle__label { color: var(--dw-text-strong); font-weight: var(--dw-font-weight-medium); }
636
634
 
637
635
  /* Compact calendar (inline in popovers): smaller day cells and type, quieter weekday row and a
638
- tighter time picker. <p-datepicker styleClass="dw-datepicker--compact" [inline]="true" />.
636
+ tighter time picker. <dw-date-picker [compact]="true" [inline]="true" />.
639
637
  Two classes per rule so they outrank PrimeNG's own (unlayered) datepicker styles. */
640
638
  .dw-datepicker--compact .p-datepicker-header { padding: 0 0 var(--dw-space-2); }
641
639
  .dw-datepicker--compact .p-datepicker-title,
@@ -675,3 +673,108 @@ body .dw-var-field .p-textarea {
675
673
  .config-form-body .toggle-label { margin: 0; cursor: pointer; }
676
674
  /* Host of a config form component inside .config-drawer-body: fill the drawer height. */
677
675
  .config-form-host { display: flex; flex: 1; flex-direction: column; min-height: 0; }
676
+
677
+ /* ── dw-select / dw-multiselect (components) ──
678
+ The host takes the screen's sizing (a width, `dw-w-full`, a grid track) and the PrimeNG control fills it; with no
679
+ width set, the host is as wide as the control would be on its own. */
680
+ .dw-select,
681
+ .dw-multiselect {
682
+ display: inline-flex;
683
+ min-width: 0;
684
+ max-width: 100%;
685
+ vertical-align: middle;
686
+ }
687
+ .dw-select > p-select,
688
+ .dw-multiselect > p-multiselect {
689
+ flex: 1 1 auto;
690
+ min-width: 0;
691
+ }
692
+ .dw-select.dw-select--fluid,
693
+ .dw-multiselect.dw-multiselect--fluid {
694
+ display: flex;
695
+ width: 100%;
696
+ }
697
+
698
+ /* ── dw-checkbox / dw-radio / dw-switch (components): the control and its label on one line (dw-check) ── */
699
+ .dw-checkbox__label--empty:empty,
700
+ .dw-radio__label--empty:empty { display: none; }
701
+
702
+ /* ── dw-number-input (component): sized by the screen like dw-select ── */
703
+ .dw-number-input { display: inline-flex; min-width: 0; max-width: 100%; vertical-align: middle; }
704
+ .dw-number-input > p-inputnumber { flex: 1 1 auto; min-width: 0; }
705
+ .dw-number-input.dw-number-input--fluid { display: flex; width: 100%; }
706
+
707
+ /* ── dw-date-picker (component): sized by the screen like dw-select; the inline calendar keeps its own width unless fluid ── */
708
+ .dw-date-picker { display: inline-flex; min-width: 0; max-width: 100%; vertical-align: middle; }
709
+ .dw-date-picker > p-datepicker { flex: 1 1 auto; min-width: 0; }
710
+ .dw-date-picker.dw-date-picker--fluid { display: flex; width: 100%; }
711
+ .dw-date-picker.dw-date-picker--inline > p-datepicker { flex: none; }
712
+ .dw-date-picker.dw-date-picker--inline.dw-date-picker--fluid > p-datepicker { flex: 1 1 auto; }
713
+ /* flush: the inline calendar loses its own box and sits on its container's surface (a popover or a panel pads it). */
714
+ .dw-date-picker.dw-date-picker--flush .p-datepicker-panel-inline {
715
+ padding: 0;
716
+ border: 0;
717
+ border-radius: 0;
718
+ background: transparent;
719
+ box-shadow: none;
720
+ }
721
+
722
+ /* ── dw-autocomplete (component): sized by the screen like dw-select; the text input fills it ──
723
+ (The extra elements outweigh PrimeNG's `width: 1%` on the input beside a dropdown button, whose styles load after ours.) */
724
+ .dw-autocomplete { display: inline-flex; min-width: 0; max-width: 100%; vertical-align: middle; }
725
+ .dw-autocomplete > p-autocomplete { display: flex; flex: 1 1 auto; min-width: 0; }
726
+ .dw-autocomplete > p-autocomplete.p-autocomplete > input.p-autocomplete-input { flex: 1 1 auto; width: auto; min-width: 0; }
727
+ .dw-autocomplete.dw-autocomplete--fluid { display: flex; width: 100%; }
728
+ /* Its list (appended to <body>, so styled by its panel classes): rows in the 14px body type, unless small. */
729
+ body .dw-autocomplete-panel:not(.dw-autocomplete-panel--sm) :is(.p-autocomplete-option, .p-autocomplete-empty-message) {
730
+ font-size: var(--dw-font-size-14);
731
+ }
732
+ /* panelWidth="field": exactly as wide as the field, as select lists are (see above): PrimeNG puts the field's width in
733
+ the overlay's inline min-width, a zero width lets it win, and long suggestions ellipsise. */
734
+ .p-overlay:has(> .p-overlay-content > .dw-autocomplete-panel--field) { width: 0; }
735
+ .dw-autocomplete-panel--field .p-autocomplete-option > * {
736
+ min-width: 0;
737
+ overflow: hidden;
738
+ text-overflow: ellipsis;
739
+ }
740
+ /* size="sm" gives it the small select rows (`.dw-autocomplete-panel--sm`, with the small select lists above). */
741
+
742
+ /* ── dw-slider (component): fills its container, in crystal's tokens ──
743
+ A 4px track in the meter's colours (dw-meter) and a 16px accent handle with a surface centre; the padding keeps the
744
+ handle clear of what sits above and below. PrimeNG draws it from these variables. */
745
+ .dw-slider {
746
+ --p-slider-track-size: 4px;
747
+ --p-slider-track-background: var(--dw-surface-pressed);
748
+ --p-slider-track-border-radius: var(--dw-radius-full);
749
+ --p-slider-range-background: var(--dw-accent);
750
+ --p-slider-handle-width: 16px;
751
+ --p-slider-handle-height: 16px;
752
+ --p-slider-handle-border-radius: var(--dw-radius-full);
753
+ --p-slider-handle-background: var(--dw-accent);
754
+ --p-slider-handle-hover-background: var(--dw-accent-hover);
755
+ --p-slider-handle-content-width: 8px;
756
+ --p-slider-handle-content-height: 8px;
757
+ --p-slider-handle-content-border-radius: var(--dw-radius-full);
758
+ --p-slider-handle-content-background: var(--dw-surface);
759
+ --p-slider-handle-content-hover-background: var(--dw-surface);
760
+ --p-slider-handle-content-shadow: none;
761
+ --p-slider-handle-focus-ring-width: 0;
762
+ --p-slider-handle-focus-ring-shadow: var(--dw-focus-ring);
763
+
764
+ display: block;
765
+ width: 100%;
766
+ padding-block: var(--dw-space-2);
767
+ }
768
+ /* PrimeNG shows a grab cursor on the handle; show it grabbing while held. */
769
+ .dw-slider .p-slider-handle:active { cursor: grabbing; }
770
+ .dw-slider.dw-slider--invalid {
771
+ --p-slider-range-background: var(--dw-danger-solid);
772
+ --p-slider-handle-background: var(--dw-danger-solid);
773
+ --p-slider-handle-hover-background: var(--dw-danger-solid);
774
+ --p-slider-handle-focus-ring-shadow: var(--dw-focus-ring-danger);
775
+ }
776
+ .dw-slider.dw-slider--vertical { display: inline-block; width: auto; height: 100%; padding-block: 0; padding-inline: var(--dw-space-2); }
777
+ .dw-slider.dw-slider--vertical > p-slider { height: 100%; }
778
+ /* p-slider used directly (until its screens move to dw-slider): a block as wide as its field. */
779
+ p-slider { display: block; width: 100%; }
780
+ .dw-slider--vertical > p-slider { width: var(--p-slider-track-size); }
@@ -215,7 +215,7 @@
215
215
  }
216
216
 
217
217
  /* Selected marker: an accent left border that follows the item's rounded corners. Only the agents
218
- list page's sidebar has it (dw-nav-list [marker], and its tags tree: dw-selected-marker); every
218
+ list page's sidebar has it (dw-nav-list [marker], and its tags tree: dw-tree [marker]); every
219
219
  other selection is the plain selected fill. An inset shadow, not a border, so the item's content
220
220
  does not shift when it is selected. */
221
221
  .dw-nav-list--marker .dw-nav-item.is-active,
@@ -547,6 +547,36 @@ dw-nav-list.dw-nav-list--minimizable { overflow: hidden; padding-bottom: 0; }
547
547
  .dw-steps__item.is-done .dw-steps__no { border-color: var(--dw-accent); background: var(--dw-accent); color: var(--dw-text-inverse); }
548
548
  .dw-steps__sep { flex: 0 0 48px; height: 1px; background: var(--dw-border-strong); }
549
549
 
550
+ /* ── dw-stepper: the component over .dw-steps. Each step is a button; a done step goes back,
551
+ an upcoming step of a linear stepper is disabled but drawn as upcoming (not faded).
552
+ --inline drops the sub-header strip (a wizard header); --md is the wizard size, --sm the strip above. ── */
553
+ .dw-stepper { display: block; min-width: 0; }
554
+ .dw-steps--inline { padding: 0; border-bottom: 0; }
555
+ .dw-steps__btn {
556
+ display: inline-flex;
557
+ align-items: center;
558
+ gap: var(--dw-space-2);
559
+ margin: calc(-1 * var(--dw-space-1));
560
+ padding: var(--dw-space-1);
561
+ border: 0;
562
+ border-radius: var(--dw-radius-md);
563
+ background: transparent;
564
+ color: inherit;
565
+ font-weight: inherit;
566
+ white-space: nowrap;
567
+ transition: color var(--dw-dur-micro) var(--dw-ease);
568
+ }
569
+ .dw-steps__btn:disabled,
570
+ .dw-steps__btn[aria-current='step'] { cursor: default; }
571
+ .dw-steps__btn:not(:disabled, [aria-current='step']):hover { color: var(--dw-text-link); }
572
+ .dw-steps__btn:focus-visible { outline: none; box-shadow: var(--dw-focus-ring); }
573
+ .dw-steps__no { flex: none; transition: background-color var(--dw-dur-micro) var(--dw-ease), border-color var(--dw-dur-micro) var(--dw-ease); }
574
+ .dw-steps__item.is-disabled { opacity: 0.5; }
575
+ .dw-steps__item.is-disabled .dw-steps__btn { cursor: not-allowed; }
576
+ .dw-steps__sep.is-done { background: var(--dw-accent); }
577
+ .dw-steps--md .dw-steps__no { width: 32px; height: 32px; border-width: 2px; font-size: var(--dw-font-size-14); }
578
+ .dw-steps--md .dw-steps__sep { height: 2px; }
579
+
550
580
  /* ── dw-rail-tabs: vertical icon-over-label tab strip at a panel edge ── */
551
581
  .dw-rail-tabs { display: flex; flex-shrink: 0; width: 64px; }
552
582
  .dw-rail-tabs__nav {
@@ -339,9 +339,24 @@ body .p-drawer-right {
339
339
  }
340
340
  .p-tooltip.dw-tooltip--multiline .p-tooltip-text { white-space: pre-line; }
341
341
  .p-tooltip.dw-tooltip--error .p-tooltip-text { background: var(--dw-status-danger-fg); color: var(--dw-text-inverse); }
342
+ /* Preview (dwTooltipVariant="preview"): a long value shown whole, up to 720 × 340px, then scrolled; line breaks kept,
343
+ long tokens broken anywhere. Pair it with dwTooltipInteractive so the pointer can scroll it. */
344
+ .p-tooltip.dw-tooltip--preview { width: auto; max-width: min(720px, 68vw); }
345
+ .p-tooltip.dw-tooltip--preview .p-tooltip-text {
346
+ display: inline-block;
347
+ box-sizing: border-box;
348
+ width: max-content;
349
+ max-width: min(720px, 68vw);
350
+ max-height: min(42vh, 340px);
351
+ overflow: auto;
352
+ white-space: pre-wrap;
353
+ overflow-wrap: anywhere;
354
+ font-size: var(--dw-font-size-12);
355
+ line-height: 1.45;
356
+ }
342
357
  .p-tooltip.dw-tooltip--error .p-tooltip-arrow { border-top-color: var(--dw-status-danger-fg); }
343
358
  /* Long code / JSON: wide, scrollable, monospace, wrapped. */
344
- .p-tooltip.dw-tooltip--code { width: min(520px, calc(100vw - 48px)); max-width: min(520px, calc(100vw - 48px)); z-index: 5000; }
359
+ .p-tooltip.dw-tooltip--code { width: min(520px, calc(100vw - 48px)); max-width: min(520px, calc(100vw - 48px)); }
345
360
  .p-tooltip.dw-tooltip--code .p-tooltip-text {
346
361
  display: block;
347
362
  width: 100%;