@deepfuture/dui-components 0.0.19 → 0.0.21

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 (52) hide show
  1. package/all.d.ts +6 -2
  2. package/all.js +12 -4
  3. package/card/card.d.ts +29 -0
  4. package/card/card.js +179 -0
  5. package/card/index.d.ts +3 -0
  6. package/card/index.js +3 -0
  7. package/checkbox/checkbox.d.ts +3 -2
  8. package/checkbox/checkbox.js +21 -46
  9. package/combobox/combobox.d.ts +3 -0
  10. package/combobox/combobox.js +21 -10
  11. package/data-table/data-table.js +4 -4
  12. package/dropzone/dropzone.js +1 -0
  13. package/field/field.d.ts +32 -0
  14. package/field/field.js +363 -0
  15. package/field/index.d.ts +1 -2
  16. package/field/index.js +1 -1
  17. package/fieldset/fieldset.d.ts +20 -0
  18. package/fieldset/fieldset.js +116 -0
  19. package/fieldset/index.d.ts +1 -0
  20. package/fieldset/index.js +1 -0
  21. package/global.d.ts +0 -2
  22. package/input/input.d.ts +4 -2
  23. package/input/input.js +27 -52
  24. package/menu/menu.d.ts +2 -0
  25. package/menu/menu.js +13 -3
  26. package/number-field/number-field.d.ts +2 -2
  27. package/number-field/number-field.js +13 -49
  28. package/package.json +10 -6
  29. package/preview-card/preview-card-popup.js +1 -0
  30. package/radio/radio.d.ts +3 -2
  31. package/radio/radio.js +22 -44
  32. package/scroll-area/scroll-area.d.ts +1 -1
  33. package/scroll-area/scroll-area.js +5 -16
  34. package/select/select.d.ts +5 -2
  35. package/select/select.js +38 -34
  36. package/separator/separator.js +1 -0
  37. package/slider/slider.d.ts +3 -0
  38. package/slider/slider.js +12 -5
  39. package/split-button/split-button.d.ts +2 -0
  40. package/split-button/split-button.js +11 -1
  41. package/stepper/stepper.d.ts +0 -2
  42. package/stepper/stepper.js +7 -38
  43. package/switch/switch.d.ts +3 -2
  44. package/switch/switch.js +16 -41
  45. package/textarea/textarea.d.ts +4 -0
  46. package/textarea/textarea.js +20 -0
  47. package/field/field-context.d.ts +0 -20
  48. package/field/field-context.js +0 -2
  49. package/link/index.d.ts +0 -3
  50. package/link/index.js +0 -3
  51. package/link/link.d.ts +0 -27
  52. package/link/link.js +0 -95
package/radio/radio.js CHANGED
@@ -34,12 +34,10 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
34
34
  return useValue ? value : void 0;
35
35
  };
36
36
  import { css, html, LitElement, nothing } from "lit";
37
- import { property, state } from "lit/decorators.js";
37
+ import { property } from "lit/decorators.js";
38
38
  import { ContextConsumer } from "@lit/context";
39
- import { consume } from "@lit/context";
40
39
  import { base } from "@deepfuture/dui-core/base";
41
40
  import { radioGroupContext } from "./radio-group-context.js";
42
- import { fieldContext } from "@deepfuture/dui-components/field";
43
41
  /** Structural styles only — layout CSS. */
44
42
  const styles = css `
45
43
  :host {
@@ -62,6 +60,7 @@ const styles = css `
62
60
  padding: 0;
63
61
  margin: 0;
64
62
  border: none;
63
+ user-select: none;
65
64
  }
66
65
 
67
66
  [part="root"][data-disabled] {
@@ -84,14 +83,6 @@ const styles = css `
84
83
  display: none;
85
84
  }
86
85
 
87
- .hidden-input {
88
- position: absolute;
89
- pointer-events: none;
90
- opacity: 0;
91
- margin: 0;
92
- width: 0;
93
- height: 0;
94
- }
95
86
  `;
96
87
  /**
97
88
  * `<dui-radio>` — A radio button input.
@@ -115,24 +106,25 @@ let DuiRadio = (() => {
115
106
  let _readOnly_decorators;
116
107
  let _readOnly_initializers = [];
117
108
  let _readOnly_extraInitializers = [];
118
- let __fieldCtx_decorators;
119
- let __fieldCtx_initializers = [];
120
- let __fieldCtx_extraInitializers = [];
121
109
  return class DuiRadio extends _classSuper {
122
110
  static {
123
111
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
124
112
  _value_decorators = [property()];
125
113
  _disabled_decorators = [property({ type: Boolean, reflect: true })];
126
114
  _readOnly_decorators = [property({ type: Boolean, reflect: true, attribute: "read-only" })];
127
- __fieldCtx_decorators = [consume({ context: fieldContext, subscribe: true }), state()];
128
115
  __esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
129
116
  __esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
130
117
  __esDecorate(this, null, _readOnly_decorators, { kind: "accessor", name: "readOnly", static: false, private: false, access: { has: obj => "readOnly" in obj, get: obj => obj.readOnly, set: (obj, value) => { obj.readOnly = value; } }, metadata: _metadata }, _readOnly_initializers, _readOnly_extraInitializers);
131
- __esDecorate(this, null, __fieldCtx_decorators, { kind: "accessor", name: "_fieldCtx", static: false, private: false, access: { has: obj => "_fieldCtx" in obj, get: obj => obj._fieldCtx, set: (obj, value) => { obj._fieldCtx = value; } }, metadata: _metadata }, __fieldCtx_initializers, __fieldCtx_extraInitializers);
132
118
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
133
119
  }
134
120
  static tagName = "dui-radio";
121
+ static formAssociated = true;
135
122
  static styles = [base, styles];
123
+ #internals;
124
+ constructor() {
125
+ super();
126
+ this.#internals = this.attachInternals();
127
+ }
136
128
  #value_accessor_storage = __runInitializers(this, _value_initializers, "");
137
129
  /** The value attribute for this radio option. */
138
130
  get value() { return this.#value_accessor_storage; }
@@ -149,16 +141,12 @@ let DuiRadio = (() => {
149
141
  context: radioGroupContext,
150
142
  subscribe: true,
151
143
  }));
152
- #_fieldCtx_accessor_storage = __runInitializers(this, __fieldCtx_initializers, void 0);
153
- get _fieldCtx() { return this.#_fieldCtx_accessor_storage; }
154
- set _fieldCtx(value) { this.#_fieldCtx_accessor_storage = value; }
155
144
  get #isChecked() {
156
145
  return this.#groupCtx.value?.value === this.value;
157
146
  }
158
147
  get #isDisabled() {
159
148
  return (this.disabled ||
160
- (this.#groupCtx.value?.disabled ?? false) ||
161
- (this._fieldCtx?.disabled ?? false));
149
+ (this.#groupCtx.value?.disabled ?? false));
162
150
  }
163
151
  get #isReadOnly() {
164
152
  return this.readOnly || (this.#groupCtx.value?.readOnly ?? false);
@@ -166,30 +154,36 @@ let DuiRadio = (() => {
166
154
  get #isRequired() {
167
155
  return this.#groupCtx.value?.required ?? false;
168
156
  }
169
- get #isInvalid() {
170
- return this._fieldCtx?.invalid ?? false;
171
- }
172
157
  connectedCallback() {
173
158
  super.connectedCallback();
174
159
  this.addEventListener("click", this.#handleHostClick);
175
160
  }
161
+ willUpdate() {
162
+ this.#syncFormValue();
163
+ }
164
+ #syncFormValue() {
165
+ if (this.#isChecked) {
166
+ this.#internals.setFormValue(this.value);
167
+ }
168
+ else {
169
+ this.#internals.setFormValue(null);
170
+ }
171
+ }
176
172
  disconnectedCallback() {
177
173
  super.disconnectedCallback();
178
174
  this.removeEventListener("click", this.#handleHostClick);
179
175
  }
180
- #handleHostClick = (__runInitializers(this, __fieldCtx_extraInitializers), (e) => {
176
+ #handleHostClick = (e) => {
181
177
  if (e.target.closest("[part='root']"))
182
178
  return;
183
179
  this.#handleClick(e);
184
- });
180
+ };
185
181
  #handleClick = (_e) => {
186
182
  if (this.#isDisabled || this.#isReadOnly)
187
183
  return;
188
184
  const ctx = this.#groupCtx.value;
189
185
  if (ctx) {
190
186
  ctx.select(this.value);
191
- this._fieldCtx?.markDirty();
192
- this._fieldCtx?.markTouched();
193
187
  }
194
188
  };
195
189
  #handleKeyDown = (e) => {
@@ -203,25 +197,20 @@ let DuiRadio = (() => {
203
197
  const isDisabled = this.#isDisabled;
204
198
  const isReadOnly = this.#isReadOnly;
205
199
  const isRequired = this.#isRequired;
206
- const isInvalid = this.#isInvalid;
207
- const controlId = this._fieldCtx?.controlId ?? "";
208
200
  return html `
209
201
  <span
210
202
  part="root"
211
203
  role="radio"
212
- id="${controlId || nothing}"
213
204
  aria-checked="${String(isChecked)}"
214
205
  aria-disabled="${isDisabled ? "true" : nothing}"
215
206
  aria-readonly="${isReadOnly ? "true" : nothing}"
216
207
  aria-required="${isRequired ? "true" : nothing}"
217
- aria-invalid="${isInvalid ? "true" : nothing}"
218
208
  tabindex="${isDisabled ? nothing : "0"}"
219
209
  ?data-checked="${isChecked}"
220
210
  ?data-unchecked="${!isChecked}"
221
211
  ?data-disabled="${isDisabled}"
222
212
  ?data-readonly="${isReadOnly}"
223
213
  ?data-required="${isRequired}"
224
- ?data-invalid="${isInvalid}"
225
214
  @click="${this.#handleClick}"
226
215
  @keydown="${this.#handleKeyDown}"
227
216
  >
@@ -232,17 +221,6 @@ let DuiRadio = (() => {
232
221
  >
233
222
  ${isChecked ? html `<span part="dot"></span>` : nothing}
234
223
  </span>
235
- <input
236
- type="radio"
237
- name="${this.#groupCtx.value?.name ?? nothing}"
238
- value="${this.value}"
239
- .checked="${isChecked}"
240
- ?disabled="${isDisabled}"
241
- ?required="${isRequired}"
242
- class="hidden-input"
243
- aria-hidden="true"
244
- tabindex="-1"
245
- />
246
224
  </span>
247
225
  <slot></slot>
248
226
  `;
@@ -11,7 +11,7 @@ type ScrollAreaOrientation = "vertical" | "horizontal" | "both";
11
11
  *
12
12
  * @cssprop [--scroll-area-max-height] - Max-height constraint.
13
13
  * @cssprop [--scroll-area-thumb-color] - Scrollbar thumb color.
14
- * @cssprop [--scroll-fade-color] - Fade overlay color.
14
+ * @cssprop [--scroll-fade-size] - Distance over which the top fade goes from transparent to opaque (default: 1.5rem).
15
15
  */
16
16
  export declare class DuiScrollArea extends LitElement {
17
17
  #private;
@@ -69,14 +69,9 @@ const styles = css `
69
69
  }
70
70
  }
71
71
 
72
- .ScrollFade {
73
- position: absolute;
74
- top: 0;
75
- left: 0;
76
- right: 0;
77
- height: 0;
78
- pointer-events: none;
79
- z-index: 1;
72
+ :host([fade]) .Viewport[data-scrolled] {
73
+ -webkit-mask-image: linear-gradient(to bottom, transparent, black var(--scroll-fade-size, 1.5rem));
74
+ mask-image: linear-gradient(to bottom, transparent, black var(--scroll-fade-size, 1.5rem));
80
75
  }
81
76
 
82
77
  .Scrollbar {
@@ -147,7 +142,7 @@ const styles = css `
147
142
  *
148
143
  * @cssprop [--scroll-area-max-height] - Max-height constraint.
149
144
  * @cssprop [--scroll-area-thumb-color] - Scrollbar thumb color.
150
- * @cssprop [--scroll-fade-color] - Fade overlay color.
145
+ * @cssprop [--scroll-fade-size] - Distance over which the top fade goes from transparent to opaque (default: 1.5rem).
151
146
  */
152
147
  let DuiScrollArea = (() => {
153
148
  var _a;
@@ -541,13 +536,7 @@ let DuiScrollArea = (() => {
541
536
  ?data-has-overflow-y="${this.#hasOverflowY}"
542
537
  ?data-scrolling="${this.#scrolling}"
543
538
  >
544
- ${this.fade
545
- ? html `<div
546
- class="ScrollFade"
547
- ?data-scrolled="${!this.#isAtTop}"
548
- ></div>`
549
- : nothing}
550
- <div class="Viewport" part="viewport" @scroll="${this.#onScroll}">
539
+ <div class="Viewport" part="viewport" ?data-scrolled="${!this.#isAtTop}" @scroll="${this.#onScroll}">
551
540
  <slot></slot>
552
541
  </div>
553
542
  ${this.#renderVerticalScrollbar()} ${this.#renderHorizontalScrollbar()}
@@ -1,6 +1,5 @@
1
1
  /** Ported from original DUI: deep-future-app/app/client/components/dui/select */
2
2
  import { LitElement, type TemplateResult } from "lit";
3
- import { type FieldContext } from "../field/field-context.js";
4
3
  export type SelectOption = {
5
4
  label: string;
6
5
  value: string;
@@ -22,7 +21,9 @@ export declare const valueChangeEvent: (detail: SelectValueChangeDetail) => Cust
22
21
  export declare class DuiSelect extends LitElement {
23
22
  #private;
24
23
  static tagName: "dui-select";
24
+ static formAssociated: boolean;
25
25
  static styles: import("lit").CSSResult[];
26
+ constructor();
26
27
  /** The available options. */
27
28
  accessor options: SelectOption[];
28
29
  /** Currently selected value. */
@@ -31,8 +32,10 @@ export declare class DuiSelect extends LitElement {
31
32
  accessor placeholder: string;
32
33
  /** Whether the select is disabled. */
33
34
  accessor disabled: boolean;
35
+ /** Position the popup so the selected item overlays the trigger (macOS-style). */
36
+ accessor alignItemToTrigger: boolean;
34
37
  /** Name for form submission. */
35
38
  accessor name: string;
36
- accessor _fieldCtx: FieldContext;
39
+ willUpdate(): void;
37
40
  render(): TemplateResult;
38
41
  }
package/select/select.js CHANGED
@@ -40,11 +40,9 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
40
40
  import { css, html, LitElement, nothing } from "lit";
41
41
  import { property, state } from "lit/decorators.js";
42
42
  import { repeat } from "lit/directives/repeat.js";
43
- import { consume } from "@lit/context";
44
43
  import { base } from "@deepfuture/dui-core/base";
45
44
  import { customEvent } from "@deepfuture/dui-core/event";
46
45
  import { FloatingPortalController } from "@deepfuture/dui-core/floating-portal-controller";
47
- import { fieldContext } from "../field/field-context.js";
48
46
  export const valueChangeEvent = customEvent("value-change", { bubbles: true, composed: true });
49
47
  /** Structural styles only — layout CSS. */
50
48
  const hostStyles = css `
@@ -143,12 +141,12 @@ let DuiSelect = (() => {
143
141
  let _disabled_decorators;
144
142
  let _disabled_initializers = [];
145
143
  let _disabled_extraInitializers = [];
144
+ let _alignItemToTrigger_decorators;
145
+ let _alignItemToTrigger_initializers = [];
146
+ let _alignItemToTrigger_extraInitializers = [];
146
147
  let _name_decorators;
147
148
  let _name_initializers = [];
148
149
  let _name_extraInitializers = [];
149
- let __fieldCtx_decorators;
150
- let __fieldCtx_initializers = [];
151
- let __fieldCtx_extraInitializers = [];
152
150
  let _private_highlightedIndex_decorators;
153
151
  let _private_highlightedIndex_initializers = [];
154
152
  let _private_highlightedIndex_extraInitializers = [];
@@ -160,20 +158,26 @@ let DuiSelect = (() => {
160
158
  _value_decorators = [property({ type: String })];
161
159
  _placeholder_decorators = [property({ type: String })];
162
160
  _disabled_decorators = [property({ type: Boolean, reflect: true })];
161
+ _alignItemToTrigger_decorators = [property({ type: Boolean, attribute: "align-item-to-trigger", reflect: true })];
163
162
  _name_decorators = [property({ type: String })];
164
- __fieldCtx_decorators = [consume({ context: fieldContext, subscribe: true }), state()];
165
163
  _private_highlightedIndex_decorators = [state()];
166
164
  __esDecorate(this, null, _options_decorators, { kind: "accessor", name: "options", static: false, private: false, access: { has: obj => "options" in obj, get: obj => obj.options, set: (obj, value) => { obj.options = value; } }, metadata: _metadata }, _options_initializers, _options_extraInitializers);
167
165
  __esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
168
166
  __esDecorate(this, null, _placeholder_decorators, { kind: "accessor", name: "placeholder", static: false, private: false, access: { has: obj => "placeholder" in obj, get: obj => obj.placeholder, set: (obj, value) => { obj.placeholder = value; } }, metadata: _metadata }, _placeholder_initializers, _placeholder_extraInitializers);
169
167
  __esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
168
+ __esDecorate(this, null, _alignItemToTrigger_decorators, { kind: "accessor", name: "alignItemToTrigger", static: false, private: false, access: { has: obj => "alignItemToTrigger" in obj, get: obj => obj.alignItemToTrigger, set: (obj, value) => { obj.alignItemToTrigger = value; } }, metadata: _metadata }, _alignItemToTrigger_initializers, _alignItemToTrigger_extraInitializers);
170
169
  __esDecorate(this, null, _name_decorators, { kind: "accessor", name: "name", static: false, private: false, access: { has: obj => "name" in obj, get: obj => obj.name, set: (obj, value) => { obj.name = value; } }, metadata: _metadata }, _name_initializers, _name_extraInitializers);
171
- __esDecorate(this, null, __fieldCtx_decorators, { kind: "accessor", name: "_fieldCtx", static: false, private: false, access: { has: obj => "_fieldCtx" in obj, get: obj => obj._fieldCtx, set: (obj, value) => { obj._fieldCtx = value; } }, metadata: _metadata }, __fieldCtx_initializers, __fieldCtx_extraInitializers);
172
170
  __esDecorate(this, _private_highlightedIndex_descriptor = { get: __setFunctionName(function () { return this.#highlightedIndex_accessor_storage; }, "#highlightedIndex", "get"), set: __setFunctionName(function (value) { this.#highlightedIndex_accessor_storage = value; }, "#highlightedIndex", "set") }, _private_highlightedIndex_decorators, { kind: "accessor", name: "#highlightedIndex", static: false, private: true, access: { has: obj => #highlightedIndex in obj, get: obj => obj.#highlightedIndex, set: (obj, value) => { obj.#highlightedIndex = value; } }, metadata: _metadata }, _private_highlightedIndex_initializers, _private_highlightedIndex_extraInitializers);
173
171
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
174
172
  }
175
173
  static tagName = "dui-select";
174
+ static formAssociated = true;
176
175
  static styles = [base, hostStyles, componentStyles];
176
+ #internals;
177
+ constructor() {
178
+ super();
179
+ this.#internals = this.attachInternals();
180
+ }
177
181
  #options_accessor_storage = __runInitializers(this, _options_initializers, []);
178
182
  /** The available options. */
179
183
  get options() { return this.#options_accessor_storage; }
@@ -190,14 +194,15 @@ let DuiSelect = (() => {
190
194
  /** Whether the select is disabled. */
191
195
  get disabled() { return this.#disabled_accessor_storage; }
192
196
  set disabled(value) { this.#disabled_accessor_storage = value; }
193
- #name_accessor_storage = (__runInitializers(this, _disabled_extraInitializers), __runInitializers(this, _name_initializers, ""));
197
+ #alignItemToTrigger_accessor_storage = (__runInitializers(this, _disabled_extraInitializers), __runInitializers(this, _alignItemToTrigger_initializers, true));
198
+ /** Position the popup so the selected item overlays the trigger (macOS-style). */
199
+ get alignItemToTrigger() { return this.#alignItemToTrigger_accessor_storage; }
200
+ set alignItemToTrigger(value) { this.#alignItemToTrigger_accessor_storage = value; }
201
+ #name_accessor_storage = (__runInitializers(this, _alignItemToTrigger_extraInitializers), __runInitializers(this, _name_initializers, ""));
194
202
  /** Name for form submission. */
195
203
  get name() { return this.#name_accessor_storage; }
196
204
  set name(value) { this.#name_accessor_storage = value; }
197
- #_fieldCtx_accessor_storage = (__runInitializers(this, _name_extraInitializers), __runInitializers(this, __fieldCtx_initializers, void 0));
198
- get _fieldCtx() { return this.#_fieldCtx_accessor_storage; }
199
- set _fieldCtx(value) { this.#_fieldCtx_accessor_storage = value; }
200
- #highlightedIndex_accessor_storage = (__runInitializers(this, __fieldCtx_extraInitializers), __runInitializers(this, _private_highlightedIndex_initializers, -1));
205
+ #highlightedIndex_accessor_storage = (__runInitializers(this, _name_extraInitializers), __runInitializers(this, _private_highlightedIndex_initializers, -1));
201
206
  get #highlightedIndex() { return _private_highlightedIndex_descriptor.get.call(this); }
202
207
  set #highlightedIndex(value) { return _private_highlightedIndex_descriptor.set.call(this, value); }
203
208
  #triggerId = (__runInitializers(this, _private_highlightedIndex_extraInitializers), `select-trigger-${crypto.randomUUID().slice(0, 8)}`);
@@ -207,17 +212,29 @@ let DuiSelect = (() => {
207
212
  matchWidth: false,
208
213
  minMatchWidth: true,
209
214
  styles: portalPopupStyles,
215
+ alignToInner: () => {
216
+ if (!this.alignItemToTrigger)
217
+ return null;
218
+ const root = this.#popup.renderRoot;
219
+ const selectedItem = root?.querySelector("[data-selected]");
220
+ return selectedItem?.querySelector(".ItemText") ?? selectedItem ?? null;
221
+ },
222
+ alignToInnerReference: () => {
223
+ if (!this.alignItemToTrigger)
224
+ return null;
225
+ return this.shadowRoot?.querySelector(".Value") ?? null;
226
+ },
210
227
  onOpen: () => {
211
228
  this.#highlightedIndex = this.#selectedIndex;
212
229
  },
213
230
  onClose: () => {
214
231
  this.#highlightedIndex = -1;
215
- this._fieldCtx?.markTouched();
216
232
  },
217
233
  renderPopup: (portal) => {
218
234
  return html `
219
235
  <div
220
236
  class="Popup"
237
+ ?data-align-inner="${this.alignItemToTrigger && this.value !== ""}"
221
238
  ?data-starting-style="${portal.isStarting}"
222
239
  ?data-ending-style="${portal.isEnding}"
223
240
  >
@@ -225,7 +242,7 @@ let DuiSelect = (() => {
225
242
  class="Listbox"
226
243
  id="${this.#listboxId}"
227
244
  role="listbox"
228
- aria-labelledby="${this._fieldCtx?.labelId ?? ""}"
245
+
229
246
  @mousedown="${this.#onListMouseDown}"
230
247
  >
231
248
  ${repeat(this.options, (option) => option.value, this.#renderItem)}
@@ -234,13 +251,10 @@ let DuiSelect = (() => {
234
251
  `;
235
252
  },
236
253
  });
237
- // ---- Computed ----
238
- get #isDisabled() {
239
- return this.disabled || (this._fieldCtx?.disabled ?? false);
240
- }
241
- get #isInvalid() {
242
- return this._fieldCtx?.invalid ?? false;
254
+ willUpdate() {
255
+ this.#internals.setFormValue(this.value);
243
256
  }
257
+ // ---- Computed ----
244
258
  get #selectedOption() {
245
259
  return this.options.find((o) => o.value === this.value);
246
260
  }
@@ -253,7 +267,7 @@ let DuiSelect = (() => {
253
267
  // ---- Event handlers ----
254
268
  #onTriggerClick = (event) => {
255
269
  event.stopPropagation();
256
- if (this.#isDisabled)
270
+ if (this.disabled)
257
271
  return;
258
272
  if (this.#popup.isOpen) {
259
273
  this.#popup.close();
@@ -263,7 +277,7 @@ let DuiSelect = (() => {
263
277
  }
264
278
  };
265
279
  #onTriggerKeyDown = (event) => {
266
- if (this.#isDisabled)
280
+ if (this.disabled)
267
281
  return;
268
282
  switch (event.key) {
269
283
  case "Enter":
@@ -346,8 +360,6 @@ let DuiSelect = (() => {
346
360
  // ---- Selection ----
347
361
  #selectOption(option) {
348
362
  this.value = option.value;
349
- this._fieldCtx?.markDirty();
350
- this._fieldCtx?.setFilled(this.value.length > 0);
351
363
  this.dispatchEvent(valueChangeEvent({ value: option.value, option }));
352
364
  this.#popup.close();
353
365
  this.#focusTrigger();
@@ -399,22 +411,17 @@ let DuiSelect = (() => {
399
411
  part="trigger"
400
412
  id="${this.#triggerId}"
401
413
  role="combobox"
402
- tabindex="${this.#isDisabled ? -1 : 0}"
414
+ tabindex="${this.disabled ? -1 : 0}"
403
415
  aria-haspopup="listbox"
404
416
  aria-expanded="${this.#popup.isOpen}"
405
417
  aria-controls="${this.#listboxId}"
406
418
  aria-activedescendant="${this.#highlightedIndex >= 0
407
419
  ? `${this.#listboxId}-option-${this.#highlightedIndex}`
408
420
  : nothing}"
409
- aria-labelledby="${this._fieldCtx?.labelId ?? ""}"
410
- aria-invalid="${this.#isInvalid}"
411
- ?data-disabled="${this.#isDisabled}"
412
- ?data-invalid="${this.#isInvalid}"
421
+ ?data-disabled="${this.disabled}"
413
422
  ?data-open="${this.#popup.isOpen}"
414
423
  @click="${this.#onTriggerClick}"
415
424
  @keydown="${this.#onTriggerKeyDown}"
416
- @focus="${() => this._fieldCtx?.setFocused(true)}"
417
- @blur="${() => this._fieldCtx?.setFocused(false)}"
418
425
  >
419
426
  <span
420
427
  class="Value"
@@ -428,9 +435,6 @@ let DuiSelect = (() => {
428
435
  </span>
429
436
  </div>
430
437
 
431
- ${this.name
432
- ? html `<input type="hidden" name="${this.name}" .value="${this.value}" />`
433
- : nothing}
434
438
  `;
435
439
  }
436
440
  };
@@ -44,6 +44,7 @@ const styles = css `
44
44
  :host([orientation="vertical"]) {
45
45
  display: inline-block;
46
46
  align-self: stretch;
47
+ height: 100%;
47
48
  }
48
49
 
49
50
  [part="root"] {
@@ -20,7 +20,9 @@ export declare const valueCommittedEvent: (detail: {
20
20
  export declare class DuiSlider extends LitElement {
21
21
  #private;
22
22
  static tagName: "dui-slider";
23
+ static formAssociated: boolean;
23
24
  static styles: import("lit").CSSResult[];
25
+ constructor();
24
26
  /** Current value. */
25
27
  accessor value: number;
26
28
  /** Minimum value. */
@@ -39,6 +41,7 @@ export declare class DuiSlider extends LitElement {
39
41
  accessor unit: string;
40
42
  /** Decimal places for value readout. Auto-inferred from `step` if not set. */
41
43
  accessor precision: number | undefined;
44
+ willUpdate(): void;
42
45
  disconnectedCallback(): void;
43
46
  render(): TemplateResult;
44
47
  }
package/slider/slider.js CHANGED
@@ -201,7 +201,13 @@ let DuiSlider = (() => {
201
201
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
202
202
  }
203
203
  static tagName = "dui-slider";
204
+ static formAssociated = true;
204
205
  static styles = [base, styles];
206
+ #internals;
207
+ constructor() {
208
+ super();
209
+ this.#internals = this.attachInternals();
210
+ }
205
211
  #value_accessor_storage = __runInitializers(this, _value_initializers, 0);
206
212
  /** Current value. */
207
213
  get value() { return this.#value_accessor_storage; }
@@ -238,6 +244,9 @@ let DuiSlider = (() => {
238
244
  /** Decimal places for value readout. Auto-inferred from `step` if not set. */
239
245
  get precision() { return this.#precision_accessor_storage; }
240
246
  set precision(value) { this.#precision_accessor_storage = value; }
247
+ willUpdate() {
248
+ this.#internals.setFormValue(String(this.value));
249
+ }
241
250
  #dragging_accessor_storage = (__runInitializers(this, _precision_extraInitializers), __runInitializers(this, _private_dragging_initializers, false));
242
251
  get #dragging() { return _private_dragging_descriptor.get.call(this); }
243
252
  set #dragging(value) { return _private_dragging_descriptor.set.call(this, value); }
@@ -275,7 +284,6 @@ let DuiSlider = (() => {
275
284
  if (this.disabled)
276
285
  return;
277
286
  event.preventDefault();
278
- this.#dragging = true;
279
287
  const newValue = this.#getValueFromPosition(event.clientX);
280
288
  if (newValue !== this.value) {
281
289
  this.value = newValue;
@@ -286,7 +294,7 @@ let DuiSlider = (() => {
286
294
  });
287
295
  #onPointerMove = (event) => {
288
296
  if (!this.#dragging)
289
- return;
297
+ this.#dragging = true;
290
298
  const newValue = this.#getValueFromPosition(event.clientX);
291
299
  if (newValue !== this.value) {
292
300
  this.value = newValue;
@@ -294,12 +302,11 @@ let DuiSlider = (() => {
294
302
  }
295
303
  };
296
304
  #onPointerUp = () => {
297
- if (!this.#dragging)
298
- return;
305
+ const wasDragging = this.#dragging;
299
306
  this.#dragging = false;
300
- this.dispatchEvent(valueCommittedEvent({ value: this.value }));
301
307
  document.removeEventListener("pointermove", this.#onPointerMove);
302
308
  document.removeEventListener("pointerup", this.#onPointerUp);
309
+ this.dispatchEvent(valueCommittedEvent({ value: this.value }));
303
310
  };
304
311
  #onKeyDown = (event) => {
305
312
  if (this.disabled)
@@ -26,6 +26,8 @@ export declare class DuiSplitButton extends LitElement {
26
26
  accessor appearance: string;
27
27
  /** Size — mapped to theme styles (e.g. xs, sm, md, lg). */
28
28
  accessor size: string;
29
+ /** Sets `min-width` on the popup panel (e.g. `"200px"`). Defaults to `"var(--space-28)"`. */
30
+ accessor popupMinWidth: string;
29
31
  /** Whether the entire split button is disabled. */
30
32
  accessor disabled: boolean;
31
33
  protected updated(): void;
@@ -136,6 +136,9 @@ let DuiSplitButton = (() => {
136
136
  let _size_decorators;
137
137
  let _size_initializers = [];
138
138
  let _size_extraInitializers = [];
139
+ let _popupMinWidth_decorators;
140
+ let _popupMinWidth_initializers = [];
141
+ let _popupMinWidth_extraInitializers = [];
139
142
  let _disabled_decorators;
140
143
  let _disabled_initializers = [];
141
144
  let _disabled_extraInitializers = [];
@@ -149,11 +152,13 @@ let DuiSplitButton = (() => {
149
152
  _variant_decorators = [property({ reflect: true })];
150
153
  _appearance_decorators = [property({ reflect: true })];
151
154
  _size_decorators = [property({ reflect: true })];
155
+ _popupMinWidth_decorators = [property({ attribute: "popup-min-width" })];
152
156
  _disabled_decorators = [property({ type: Boolean, reflect: true })];
153
157
  _private_highlightedIndex_decorators = [state()];
154
158
  __esDecorate(this, null, _variant_decorators, { kind: "accessor", name: "variant", static: false, private: false, access: { has: obj => "variant" in obj, get: obj => obj.variant, set: (obj, value) => { obj.variant = value; } }, metadata: _metadata }, _variant_initializers, _variant_extraInitializers);
155
159
  __esDecorate(this, null, _appearance_decorators, { kind: "accessor", name: "appearance", static: false, private: false, access: { has: obj => "appearance" in obj, get: obj => obj.appearance, set: (obj, value) => { obj.appearance = value; } }, metadata: _metadata }, _appearance_initializers, _appearance_extraInitializers);
156
160
  __esDecorate(this, null, _size_decorators, { kind: "accessor", name: "size", static: false, private: false, access: { has: obj => "size" in obj, get: obj => obj.size, set: (obj, value) => { obj.size = value; } }, metadata: _metadata }, _size_initializers, _size_extraInitializers);
161
+ __esDecorate(this, null, _popupMinWidth_decorators, { kind: "accessor", name: "popupMinWidth", static: false, private: false, access: { has: obj => "popupMinWidth" in obj, get: obj => obj.popupMinWidth, set: (obj, value) => { obj.popupMinWidth = value; } }, metadata: _metadata }, _popupMinWidth_initializers, _popupMinWidth_extraInitializers);
157
162
  __esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
158
163
  __esDecorate(this, _private_highlightedIndex_descriptor = { get: __setFunctionName(function () { return this.#highlightedIndex_accessor_storage; }, "#highlightedIndex", "get"), set: __setFunctionName(function (value) { this.#highlightedIndex_accessor_storage = value; }, "#highlightedIndex", "set") }, _private_highlightedIndex_decorators, { kind: "accessor", name: "#highlightedIndex", static: false, private: true, access: { has: obj => #highlightedIndex in obj, get: obj => obj.#highlightedIndex, set: (obj, value) => { obj.#highlightedIndex = value; } }, metadata: _metadata }, _private_highlightedIndex_initializers, _private_highlightedIndex_extraInitializers);
159
164
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -172,7 +177,11 @@ let DuiSplitButton = (() => {
172
177
  /** Size — mapped to theme styles (e.g. xs, sm, md, lg). */
173
178
  get size() { return this.#size_accessor_storage; }
174
179
  set size(value) { this.#size_accessor_storage = value; }
175
- #disabled_accessor_storage = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _disabled_initializers, false));
180
+ #popupMinWidth_accessor_storage = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _popupMinWidth_initializers, "var(--space-28)"));
181
+ /** Sets `min-width` on the popup panel (e.g. `"200px"`). Defaults to `"var(--space-28)"`. */
182
+ get popupMinWidth() { return this.#popupMinWidth_accessor_storage; }
183
+ set popupMinWidth(value) { this.#popupMinWidth_accessor_storage = value; }
184
+ #disabled_accessor_storage = (__runInitializers(this, _popupMinWidth_extraInitializers), __runInitializers(this, _disabled_initializers, false));
176
185
  /** Whether the entire split button is disabled. */
177
186
  get disabled() { return this.#disabled_accessor_storage; }
178
187
  set disabled(value) { this.#disabled_accessor_storage = value; }
@@ -195,6 +204,7 @@ let DuiSplitButton = (() => {
195
204
  renderPopup: (portal) => html `
196
205
  <div
197
206
  class="Popup"
207
+ style="${this.popupMinWidth ? `min-width:${this.popupMinWidth}` : ""}"
198
208
  ?data-starting-style="${portal.isStarting}"
199
209
  ?data-ending-style="${portal.isEnding}"
200
210
  >
@@ -1,5 +1,4 @@
1
1
  import { LitElement, type TemplateResult } from "lit";
2
- import { type FieldContext } from "@deepfuture/dui-components/field";
3
2
  export declare const valueChangeEvent: (detail: {
4
3
  value: number;
5
4
  }) => CustomEvent<{
@@ -38,7 +37,6 @@ export declare class DuiStepper extends LitElement {
38
37
  accessor readOnly: boolean;
39
38
  accessor required: boolean;
40
39
  accessor name: string | undefined;
41
- accessor _fieldCtx: FieldContext;
42
40
  connectedCallback(): void;
43
41
  willUpdate(): void;
44
42
  render(): TemplateResult;