@fusion.dev/helix-headless 0.47.1 → 0.48.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.
@@ -89,18 +89,28 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
89
89
  this.handleLeave = () => {
90
90
  this.previewing = undefined;
91
91
  };
92
+ this.warned = false;
93
+ }
94
+ get floor() {
95
+ if (!Number.isFinite(this.min)) {
96
+ return 0;
97
+ }
98
+ return Math.min(Math.max(this.min, 0), Math.max(this.max, 0));
92
99
  }
93
100
  get score() {
94
101
  const parsed = Number.parseFloat(this.value);
95
102
  if (!Number.isFinite(parsed)) {
96
- return 0;
103
+ return this.floor;
97
104
  }
98
- return Math.min(Math.max(parsed, 0), this.max);
105
+ return Math.min(Math.max(parsed, this.floor), this.max);
99
106
  }
100
- get steps() {
107
+ get positions() {
101
108
  const count = Math.max(1, Math.round(this.max / this.step));
102
109
  return Array.from({ length: count }, (_, index) => Number(((index + 1) * this.step).toFixed(2)));
103
110
  }
111
+ get steps() {
112
+ return this.positions.filter((score) => score >= this.floor);
113
+ }
104
114
  get options() {
105
115
  const found = [...(this.renderRoot?.querySelectorAll('[part="option"]') ?? [])];
106
116
  return found.map((element) => ({
@@ -157,6 +167,12 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
157
167
  updated(changed) {
158
168
  super.updated?.(changed);
159
169
  this.sync();
170
+ if (this.floor !== this.min && !this.warned) {
171
+ this.warned = true;
172
+ console.warn(`<${this.localName}> has min="${this.min}", which is not a score between 0 and its max of ` +
173
+ `${this.max}, so the lowest score it offers is ${this.floor}. Put min inside the marks, ` +
174
+ 'or move max.');
175
+ }
160
176
  }
161
177
  willUpdate(changed) {
162
178
  super.willUpdate(changed);
@@ -177,12 +193,16 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
177
193
  const first = this.steps[0];
178
194
  const mark = (layer, index) => html `<span part="mark">${this.markFor(layer, index)}</span>`;
179
195
  const marksIn = (layer) => Array.from({ length: count }, (_, index) => mark(layer, index));
180
- return html `<div
196
+ return html `${this.renderGroupLabel()}
197
+ <div part="field">
198
+ <div
181
199
  part="marks"
182
200
  role=${this.readOnly ? 'img' : 'radiogroup'}
183
201
  aria-label=${this.readOnly ? said : this.label || nothing}
202
+ aria-describedby=${this.describedBy ?? nothing}
203
+ aria-required=${this.required && !this.readOnly ? 'true' : nothing}
184
204
  data-slotted=${this.markSource ? '' : nothing}
185
- style=${`--rating-full: ${Math.floor(showing)}; --rating-part: ${showing - Math.floor(showing)}; --rating-steps: ${this.steps.length}`}
205
+ style=${`--rating-full: ${Math.floor(showing)}; --rating-part: ${showing - Math.floor(showing)}; --rating-steps: ${this.positions.length}`}
186
206
  @pointercancel=${this.handleDragCancel}
187
207
  @pointerdown=${this.handleDragStart}
188
208
  @pointermove=${this.handleDragMove}
@@ -207,7 +227,10 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
207
227
  >
208
228
  ${this.readOnly
209
229
  ? nothing
210
- : this.steps.map((score, index) => html `<button
230
+ :
231
+ this.positions.map((score, index) => score < this.floor
232
+ ? nothing
233
+ : html `<button
211
234
  part="option"
212
235
  role="radio"
213
236
  type="button"
@@ -216,7 +239,9 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
216
239
  data-score=${String(score)}
217
240
  style=${`--rating-step: ${index + 1}`}
218
241
  ?disabled=${this.isDisabled}
219
- tabindex=${this.score === score || (this.score === 0 && score === first) ? '0' : '-1'}
242
+ tabindex=${this.score === score || (this.score === this.floor && score === first)
243
+ ? '0'
244
+ : '-1'}
220
245
  @click=${this.handleOptionClick}
221
246
  @pointerenter=${this.handleOptionEnter}
222
247
  ></button>`)}
@@ -227,7 +252,9 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
227
252
  aria-hidden="true"
228
253
  >${showing}</span
229
254
  >`
230
- : nothing}`;
255
+ : nothing}
256
+ </div>
257
+ ${this.renderHint()}${this.renderFeedback()}`;
231
258
  }
232
259
  get valueAsNumber() {
233
260
  return asNumber(this.value);
@@ -43,6 +43,19 @@ declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fu
43
43
  * string, which is the whole reason the shape is that one. What each row *says*
44
44
  * is the reader's own convention, from `Intl`.
45
45
  *
46
+ * **The chosen row is marked in the markup, not worked out on upgrade.** The
47
+ * listbox is handed the answer as `.value`, and lit-ssr serializes no property
48
+ * binding, so a served `value="09:30"` shipped ninety-six rows with nothing on
49
+ * any of them saying which one it was: the mark arrived when the listbox
50
+ * upgraded and ran `place`. So each option is given `selected` as well, which
51
+ * is an attribute binding and does serve, and which the listbox then agrees
52
+ * with rather than fights, `place` writing the same answer from the same value.
53
+ * `selected` is the declaration a consumer writes on `Option` anywhere else in
54
+ * the library, and Date Picker expresses its own choice as markup for the same
55
+ * reason: a picker whose answer is invisible until a script runs is a picker
56
+ * whose answer is not served at all. Both readings do it, the list and the
57
+ * wheels, a wheel being a listbox like the list is.
58
+ *
46
59
  * @since 0.1.0
47
60
  * @status experimental
48
61
  * @category forms
@@ -248,6 +248,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
248
248
  label: option-label, text: option-text"
249
249
  value=${one}
250
250
  ?disabled=${barred(one)}
251
+ ?selected=${one === value}
251
252
  >${written(one)}</${this.tag('option')}
252
253
  >`)}
253
254
  </${this.tag('listbox')}>`;
@@ -291,6 +292,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
291
292
  label: option-label, text: option-text"
292
293
  value=${at}
293
294
  ?disabled=${this.barred(at)}
295
+ ?selected=${this.held.includes(at)}
294
296
  data-between=${between ? '' : nothing}
295
297
  >${writeTime(at, this.locale)}</${this.tag('option')}
296
298
  >`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-headless",
3
3
  "description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
4
- "version": "0.47.1",
4
+ "version": "0.48.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -46,14 +46,14 @@
46
46
  },
47
47
  "bugs": "https://github.com/project-fusion/fusion/issues",
48
48
  "dependencies": {
49
- "@fusion.dev/helix-core": "0.47.1",
50
- "@fusion.dev/helix-icons": "0.47.1",
49
+ "@fusion.dev/helix-core": "0.48.0",
50
+ "@fusion.dev/helix-icons": "0.48.0",
51
51
  "uqr": "^0.1.3"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@custom-elements-manifest/analyzer": "^0.11.0",
55
- "@fusion.dev/helix-cem": "0.47.1",
56
- "@fusion.dev/helix-testing": "0.47.1",
55
+ "@fusion.dev/helix-cem": "0.48.0",
56
+ "@fusion.dev/helix-testing": "0.48.0",
57
57
  "@fusion.dev/scripts": "0.0.0-dev",
58
58
  "@happy-dom/global-registrator": "^20.0.0",
59
59
  "@lit-labs/ssr": "^4.1.0",