@fusion.dev/helix-core 0.185.0 → 0.186.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.
- package/custom-elements.json +190 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/mixins/mixin-field/mixin-field.js +2 -3
- package/dist/utils/audit-name/audit-name-off.d.ts +5 -1
- package/dist/utils/audit-name/audit-name-off.js +2 -0
- package/dist/utils/audit-name/audit-name.d.ts +19 -0
- package/dist/utils/audit-name/audit-name.js +45 -0
- package/dist/utils/draw-dismiss/draw-dismiss.js +2 -2
- package/dist/utils/draw-icon/draw-icon.d.ts +1 -1
- package/dist/utils/glyph-path/glyph-path.d.ts +18 -0
- package/dist/utils/glyph-path/glyph-path.js +1 -0
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +9 -0
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +4 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
package/custom-elements.json
CHANGED
|
@@ -14167,6 +14167,14 @@
|
|
|
14167
14167
|
"package": "#audit-children"
|
|
14168
14168
|
}
|
|
14169
14169
|
},
|
|
14170
|
+
{
|
|
14171
|
+
"kind": "js",
|
|
14172
|
+
"name": "auditIconControl",
|
|
14173
|
+
"declaration": {
|
|
14174
|
+
"name": "auditIconControl",
|
|
14175
|
+
"package": "#audit-name"
|
|
14176
|
+
}
|
|
14177
|
+
},
|
|
14170
14178
|
{
|
|
14171
14179
|
"kind": "js",
|
|
14172
14180
|
"name": "auditName",
|
|
@@ -17439,6 +17447,14 @@
|
|
|
17439
17447
|
"module": "./utils/format-numbers/format-numbers.js"
|
|
17440
17448
|
}
|
|
17441
17449
|
},
|
|
17450
|
+
{
|
|
17451
|
+
"kind": "js",
|
|
17452
|
+
"name": "glyphPath",
|
|
17453
|
+
"declaration": {
|
|
17454
|
+
"name": "glyphPath",
|
|
17455
|
+
"module": "./utils/glyph-path/glyph-path.js"
|
|
17456
|
+
}
|
|
17457
|
+
},
|
|
17442
17458
|
{
|
|
17443
17459
|
"kind": "js",
|
|
17444
17460
|
"name": "GridBounds",
|
|
@@ -18519,6 +18535,14 @@
|
|
|
18519
18535
|
"module": "./utils/scroll-item-into-view/scroll-item-into-view.js"
|
|
18520
18536
|
}
|
|
18521
18537
|
},
|
|
18538
|
+
{
|
|
18539
|
+
"kind": "js",
|
|
18540
|
+
"name": "scrollWithin",
|
|
18541
|
+
"declaration": {
|
|
18542
|
+
"name": "scrollWithin",
|
|
18543
|
+
"module": "./utils/scroll-item-into-view/scroll-item-into-view.js"
|
|
18544
|
+
}
|
|
18545
|
+
},
|
|
18522
18546
|
{
|
|
18523
18547
|
"kind": "js",
|
|
18524
18548
|
"name": "lockScroll",
|
|
@@ -26107,6 +26131,22 @@
|
|
|
26107
26131
|
"type": {
|
|
26108
26132
|
"text": "typeof audit"
|
|
26109
26133
|
}
|
|
26134
|
+
},
|
|
26135
|
+
{
|
|
26136
|
+
"kind": "function",
|
|
26137
|
+
"name": "auditField",
|
|
26138
|
+
"description": "The field's missing-name warning in a production build: nothing.",
|
|
26139
|
+
"type": {
|
|
26140
|
+
"text": "typeof field"
|
|
26141
|
+
}
|
|
26142
|
+
},
|
|
26143
|
+
{
|
|
26144
|
+
"kind": "function",
|
|
26145
|
+
"name": "auditIconControl",
|
|
26146
|
+
"description": "The warning about a control named by a decorative icon, in a production build: nothing.",
|
|
26147
|
+
"type": {
|
|
26148
|
+
"text": "typeof iconControl"
|
|
26149
|
+
}
|
|
26110
26150
|
}
|
|
26111
26151
|
],
|
|
26112
26152
|
"exports": [
|
|
@@ -26117,6 +26157,22 @@
|
|
|
26117
26157
|
"name": "auditName",
|
|
26118
26158
|
"module": "src/utils/audit-name/audit-name-off.ts"
|
|
26119
26159
|
}
|
|
26160
|
+
},
|
|
26161
|
+
{
|
|
26162
|
+
"kind": "js",
|
|
26163
|
+
"name": "auditField",
|
|
26164
|
+
"declaration": {
|
|
26165
|
+
"name": "auditField",
|
|
26166
|
+
"module": "src/utils/audit-name/audit-name-off.ts"
|
|
26167
|
+
}
|
|
26168
|
+
},
|
|
26169
|
+
{
|
|
26170
|
+
"kind": "js",
|
|
26171
|
+
"name": "auditIconControl",
|
|
26172
|
+
"declaration": {
|
|
26173
|
+
"name": "auditIconControl",
|
|
26174
|
+
"module": "src/utils/audit-name/audit-name-off.ts"
|
|
26175
|
+
}
|
|
26120
26176
|
}
|
|
26121
26177
|
]
|
|
26122
26178
|
},
|
|
@@ -26192,6 +26248,48 @@
|
|
|
26192
26248
|
}
|
|
26193
26249
|
],
|
|
26194
26250
|
"description": "Warns once, through the runtime channel, when a control has no accessible\nname: none on the host, and none on the control inside it.\n\n**Read a task after the control first renders**, so a label that arrives in\nthe same pass counts: slotted, written by a framework beside the control, or\nforwarded from a `<label for>`. One timer serves every control that asked in\nthat task, and an element is read once in its life, so a name taken away\nlater is not reported.\n\n**In a development build only.** Every field and Button calls it, so in\nproduction it would cost each of them about 450 bytes gzip for a sentence\nmeant for the person writing the page. Core imports it as `#audit-name`,\nwhich `package.json` resolves here under the `development` condition (Vite\nand webpack in development, `bun --conditions development`,\n`node --conditions=development`) and to `audit-name-off.ts`, which does\nnothing, everywhere else. Like every runtime warning it reports through the\none channel, and a project that wants the console quiet tells\n`@fusion.dev/helix-diagnostics` with `core.warnings: false`.\n\n`control` returns the control inside the host, `null` where the host is the\ncontrol, or `undefined` where the control is not drawn at the moment of the\nread: the host is then read again the next time it asks.\n\n`also` answers for a name the component gave through its internals, which no\nattribute shows. `fix` is the sentence that says how to name it, or its key\nin `FIXES`. `shown` is the part where the control shows its value or its\nplaceholder, whose words are no name."
|
|
26251
|
+
},
|
|
26252
|
+
{
|
|
26253
|
+
"kind": "function",
|
|
26254
|
+
"name": "auditField",
|
|
26255
|
+
"return": {
|
|
26256
|
+
"type": {
|
|
26257
|
+
"text": "void"
|
|
26258
|
+
}
|
|
26259
|
+
},
|
|
26260
|
+
"parameters": [
|
|
26261
|
+
{
|
|
26262
|
+
"name": "host",
|
|
26263
|
+
"type": {
|
|
26264
|
+
"text": "HTMLElement"
|
|
26265
|
+
}
|
|
26266
|
+
}
|
|
26267
|
+
],
|
|
26268
|
+
"description": "Asks whether a field is named, as `auditName` does, with what every field\nanswers it by: the control its label points at, a label it was given or one\nslotted with words in it, and the `value` part, whose words are no name.\n\n**Here rather than at the call in `MixinField`**, for the reason `FIXES` is\nhere: the call is a no-op in production, and the closures it was handed were\nin every field's bundle anyway."
|
|
26269
|
+
},
|
|
26270
|
+
{
|
|
26271
|
+
"kind": "function",
|
|
26272
|
+
"name": "auditIconControl",
|
|
26273
|
+
"return": {
|
|
26274
|
+
"type": {
|
|
26275
|
+
"text": "void"
|
|
26276
|
+
}
|
|
26277
|
+
},
|
|
26278
|
+
"parameters": [
|
|
26279
|
+
{
|
|
26280
|
+
"name": "icon",
|
|
26281
|
+
"type": {
|
|
26282
|
+
"text": "HTMLElement"
|
|
26283
|
+
}
|
|
26284
|
+
},
|
|
26285
|
+
{
|
|
26286
|
+
"name": "label",
|
|
26287
|
+
"type": {
|
|
26288
|
+
"text": "string"
|
|
26289
|
+
}
|
|
26290
|
+
}
|
|
26291
|
+
],
|
|
26292
|
+
"description": "Asks the button or link a decorative icon sits in whether it has a name.\nIts content is the icon alone where it has none, and the warning says so.\n`label` is the icon's own, which names it, and the control with it.\n\nWords or a named picture beside the icon name the control from its\ncontent, which the audit reads for a button and not for a link."
|
|
26195
26293
|
}
|
|
26196
26294
|
],
|
|
26197
26295
|
"exports": [
|
|
@@ -26210,6 +26308,22 @@
|
|
|
26210
26308
|
"name": "auditName",
|
|
26211
26309
|
"module": "src/utils/audit-name/audit-name.ts"
|
|
26212
26310
|
}
|
|
26311
|
+
},
|
|
26312
|
+
{
|
|
26313
|
+
"kind": "js",
|
|
26314
|
+
"name": "auditField",
|
|
26315
|
+
"declaration": {
|
|
26316
|
+
"name": "auditField",
|
|
26317
|
+
"module": "src/utils/audit-name/audit-name.ts"
|
|
26318
|
+
}
|
|
26319
|
+
},
|
|
26320
|
+
{
|
|
26321
|
+
"kind": "js",
|
|
26322
|
+
"name": "auditIconControl",
|
|
26323
|
+
"declaration": {
|
|
26324
|
+
"name": "auditIconControl",
|
|
26325
|
+
"module": "src/utils/audit-name/audit-name.ts"
|
|
26326
|
+
}
|
|
26213
26327
|
}
|
|
26214
26328
|
]
|
|
26215
26329
|
},
|
|
@@ -29212,7 +29326,7 @@
|
|
|
29212
29326
|
}
|
|
29213
29327
|
}
|
|
29214
29328
|
],
|
|
29215
|
-
"description": "Draws an icon that was imported by name, rather than looked up by string.\n\n**`<helix-icon name=\"play\">` looks its glyph up in a registry at runtime.**\nThe registry holds the whole set once `@fusion.dev/helix-headless/icon
|
|
29329
|
+
"description": "Draws an icon that was imported by name, rather than looked up by string.\n\n**`<helix-icon name=\"play\">` looks its glyph up in a registry at runtime.**\nThe registry holds the whole set once `@fusion.dev/helix-headless/icon`\nregisters it, and no bundler can shake a lookup by string, so every consumer\nof any component that renders one ships all of the generated path data: measured at 50,814 bytes raw and 15,010 gzipped for 102 icons, and\ngrowing about 147 gzipped bytes per icon added. A named import ships one path\nand is checked when the package compiles rather than by a name lookup later.\n\nThat element still earns its place as *public* API, where a consumer writes a\nname in markup and a lookup is the only thing that can work, and\n`@fusion.dev/helix-headless/icon/root` names glyphs without registering the\nset. What it should not be is how this library draws its own marks.\n\n**There is no `part` option, and there cannot be.** A part name behind an\ninterpolation is a part the manifest cannot see, so the component's own\nstylesheet matches nothing and the part stops being\nAPI. `Trend` needs one and therefore writes its `<svg>` out with a literal\n`part=\"icon\"`, which is the honest cost: a helper can carry everything except\nthe thing static analysis has to read.\n\n`data-glyph` rather than a class, because the sizing lives in\n`helix/styled/src/shared/glyph/glyph.css` and a component's own `--icon-size`\ndeclarations have to keep working untouched: the variable was read by\n`helix-icon`, and now it is read by the rule that sheet carries. Without it\nevery one of those declarations would go quietly dead and every mark in six\nfamilies would change size."
|
|
29216
29330
|
}
|
|
29217
29331
|
],
|
|
29218
29332
|
"exports": [
|
|
@@ -30093,6 +30207,40 @@
|
|
|
30093
30207
|
}
|
|
30094
30208
|
]
|
|
30095
30209
|
},
|
|
30210
|
+
{
|
|
30211
|
+
"kind": "javascript-module",
|
|
30212
|
+
"path": "src/utils/glyph-path/glyph-path.ts",
|
|
30213
|
+
"declarations": [
|
|
30214
|
+
{
|
|
30215
|
+
"kind": "function",
|
|
30216
|
+
"name": "glyphPath",
|
|
30217
|
+
"return": {
|
|
30218
|
+
"type": {
|
|
30219
|
+
"text": "string"
|
|
30220
|
+
}
|
|
30221
|
+
},
|
|
30222
|
+
"parameters": [
|
|
30223
|
+
{
|
|
30224
|
+
"name": "glyph",
|
|
30225
|
+
"type": {
|
|
30226
|
+
"text": "{ readonly content: string }"
|
|
30227
|
+
}
|
|
30228
|
+
}
|
|
30229
|
+
],
|
|
30230
|
+
"description": "The `d` of a glyph drawn as one path, for a template that writes the\n`<path>` itself.\n\n**For a fixed glyph, which needs no directive.** `unsafeSVG(glyph.content)`\nparses markup a template could have written, and the directive and its base\nclass come to about 1,000 bytes in a bundle that draws nothing else through\nit. Measured October 8, 2026, bundled alone, brotli: Dialog and Tag were\neach about 300 bytes larger for the cross alone, and Tabs about 350 for the\ncross and its four carets.\n\nA glyph of more than one element keeps `unsafeSVG`, which is what Icon draws\nevery glyph with. `glyph-path.pure.spec.ts` holds each glyph drawn this way\nto a single path, so a set that redraws one in two cannot lose half of it."
|
|
30231
|
+
}
|
|
30232
|
+
],
|
|
30233
|
+
"exports": [
|
|
30234
|
+
{
|
|
30235
|
+
"kind": "js",
|
|
30236
|
+
"name": "glyphPath",
|
|
30237
|
+
"declaration": {
|
|
30238
|
+
"name": "glyphPath",
|
|
30239
|
+
"module": "src/utils/glyph-path/glyph-path.ts"
|
|
30240
|
+
}
|
|
30241
|
+
}
|
|
30242
|
+
]
|
|
30243
|
+
},
|
|
30096
30244
|
{
|
|
30097
30245
|
"kind": "javascript-module",
|
|
30098
30246
|
"path": "src/utils/grid-move/grid-move.ts",
|
|
@@ -33418,6 +33566,37 @@
|
|
|
33418
33566
|
}
|
|
33419
33567
|
}
|
|
33420
33568
|
]
|
|
33569
|
+
},
|
|
33570
|
+
{
|
|
33571
|
+
"kind": "function",
|
|
33572
|
+
"name": "scrollWithin",
|
|
33573
|
+
"return": {
|
|
33574
|
+
"type": {
|
|
33575
|
+
"text": "void"
|
|
33576
|
+
}
|
|
33577
|
+
},
|
|
33578
|
+
"parameters": [
|
|
33579
|
+
{
|
|
33580
|
+
"name": "item",
|
|
33581
|
+
"type": {
|
|
33582
|
+
"text": "HTMLElement"
|
|
33583
|
+
}
|
|
33584
|
+
},
|
|
33585
|
+
{
|
|
33586
|
+
"name": "scroller",
|
|
33587
|
+
"type": {
|
|
33588
|
+
"text": "HTMLElement"
|
|
33589
|
+
}
|
|
33590
|
+
},
|
|
33591
|
+
{
|
|
33592
|
+
"name": "{ padding = 0, smooth = false }",
|
|
33593
|
+
"default": "{}",
|
|
33594
|
+
"type": {
|
|
33595
|
+
"text": "Omit<ScrollItemOptions, 'scroller'>"
|
|
33596
|
+
}
|
|
33597
|
+
}
|
|
33598
|
+
],
|
|
33599
|
+
"description": "`scrollItemIntoView` in a scroller the caller already holds.\n\n**For a family that always knows its scroller**, as Tabs knows its strip:\n`scrollingAncestor` and the walk it takes are then in no bundle of its own,\nwhere the fallback carried them into every one. Measured October 8, 2026,\nbundled alone, brotli: about 165 bytes on Tabs."
|
|
33421
33600
|
}
|
|
33422
33601
|
],
|
|
33423
33602
|
"exports": [
|
|
@@ -33444,6 +33623,14 @@
|
|
|
33444
33623
|
"name": "scrollItemIntoView",
|
|
33445
33624
|
"module": "src/utils/scroll-item-into-view/scroll-item-into-view.ts"
|
|
33446
33625
|
}
|
|
33626
|
+
},
|
|
33627
|
+
{
|
|
33628
|
+
"kind": "js",
|
|
33629
|
+
"name": "scrollWithin",
|
|
33630
|
+
"declaration": {
|
|
33631
|
+
"name": "scrollWithin",
|
|
33632
|
+
"module": "src/utils/scroll-item-into-view/scroll-item-into-view.ts"
|
|
33633
|
+
}
|
|
33447
33634
|
}
|
|
33448
33635
|
]
|
|
33449
33636
|
},
|
|
@@ -35496,7 +35683,7 @@
|
|
|
35496
35683
|
"type": {
|
|
35497
35684
|
"text": "string"
|
|
35498
35685
|
},
|
|
35499
|
-
"default": "'0.
|
|
35686
|
+
"default": "'0.186.0'",
|
|
35500
35687
|
"description": "The version of the package this file ships in.\n\n**Written by `fusion/scripts/version.mjs`** wherever it writes the package's\n`package.json`, and stamped back into the repository beside it after a\nrelease, so the two never disagree; a spec beside this holds them to it.\nNothing else in the running code knows which release it is."
|
|
35501
35688
|
}
|
|
35502
35689
|
],
|
|
@@ -36132,6 +36319,6 @@
|
|
|
36132
36319
|
],
|
|
36133
36320
|
"helix": {
|
|
36134
36321
|
"package": "@fusion.dev/helix-core",
|
|
36135
|
-
"version": "0.
|
|
36322
|
+
"version": "0.186.0"
|
|
36136
36323
|
}
|
|
36137
36324
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { auditChildren, auditEntry } from '@fusion.dev/helix-core/audits/children';
|
|
2
|
-
export { auditName } from '@fusion.dev/helix-core/audits/name';
|
|
2
|
+
export { auditIconControl, auditName } from '@fusion.dev/helix-core/audits/name';
|
|
3
3
|
export { auditNesting } from '@fusion.dev/helix-core/audits/nesting';
|
|
4
4
|
export { auditServed } from '@fusion.dev/helix-core/audits/served';
|
|
5
5
|
export { AriaForwardController, type AriaForwardOptions, } from './controllers/aria-forward-controller/aria-forward-controller.js';
|
|
@@ -95,6 +95,7 @@ export { focusTarget } from './utils/focus-target/focus-target.js';
|
|
|
95
95
|
export { deepActiveElement, deepContains, focusableElements, PRESSABLE, pressedOwnControl, } from './utils/focusable-elements/focusable-elements.js';
|
|
96
96
|
export { afterLinkFollowed, followLink, type LinkModifiers, type LinkTarget, } from './utils/follow-link/follow-link.js';
|
|
97
97
|
export { formatNumbers } from './utils/format-numbers/format-numbers.js';
|
|
98
|
+
export { glyphPath } from './utils/glyph-path/glyph-path.js';
|
|
98
99
|
export { type GridBounds, type GridKeys, type GridPlace, gridMove, pageRows, } from './utils/grid-move/grid-move.js';
|
|
99
100
|
export { headingIn, headingsIn } from './utils/heading-in/heading-in.js';
|
|
100
101
|
export { headingLevel } from './utils/heading-level/heading-level.js';
|
|
@@ -137,7 +138,7 @@ export { valueAt } from './utils/row-data/row-data.js';
|
|
|
137
138
|
export { type Arranged, type Axis, arranged, cycle, type DataRow, filter, format, type Ordered, ordered, parse, type Sort, sort, window_, } from './utils/row-order/row-order.js';
|
|
138
139
|
export { extent, linear, type Scale } from './utils/scale/scale.js';
|
|
139
140
|
export { ticks } from './utils/scale/ticks.js';
|
|
140
|
-
export { type ScrollerOptions, type ScrollItemOptions, type ScrollRoomOptions, scrollItemIntoView, scrollingAncestor, scrollRoom, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
|
|
141
|
+
export { type ScrollerOptions, type ScrollItemOptions, type ScrollRoomOptions, scrollItemIntoView, scrollingAncestor, scrollRoom, scrollWithin, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
|
|
141
142
|
export { lockScroll, scrollHolders, unlockScroll } from './utils/scroll-lock/scroll-lock.js';
|
|
142
143
|
export { slotFilled, slotNamed } from './utils/slot-filled/slot-filled.js';
|
|
143
144
|
export { SlottedGlyph } from './utils/slotted-glyph/slotted-glyph.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { auditChildren, auditEntry } from '@fusion.dev/helix-core/audits/children';
|
|
2
|
-
export { auditName } from '@fusion.dev/helix-core/audits/name';
|
|
2
|
+
export { auditIconControl, auditName } from '@fusion.dev/helix-core/audits/name';
|
|
3
3
|
export { auditNesting } from '@fusion.dev/helix-core/audits/nesting';
|
|
4
4
|
export { auditServed } from '@fusion.dev/helix-core/audits/served';
|
|
5
5
|
export { AriaForwardController, } from './controllers/aria-forward-controller/aria-forward-controller.js';
|
|
@@ -95,6 +95,7 @@ export { focusTarget } from './utils/focus-target/focus-target.js';
|
|
|
95
95
|
export { deepActiveElement, deepContains, focusableElements, PRESSABLE, pressedOwnControl, } from './utils/focusable-elements/focusable-elements.js';
|
|
96
96
|
export { afterLinkFollowed, followLink, } from './utils/follow-link/follow-link.js';
|
|
97
97
|
export { formatNumbers } from './utils/format-numbers/format-numbers.js';
|
|
98
|
+
export { glyphPath } from './utils/glyph-path/glyph-path.js';
|
|
98
99
|
export { gridMove, pageRows, } from './utils/grid-move/grid-move.js';
|
|
99
100
|
export { headingIn, headingsIn } from './utils/heading-in/heading-in.js';
|
|
100
101
|
export { headingLevel } from './utils/heading-level/heading-level.js';
|
|
@@ -137,7 +138,7 @@ export { valueAt } from './utils/row-data/row-data.js';
|
|
|
137
138
|
export { arranged, cycle, filter, format, ordered, parse, sort, window_, } from './utils/row-order/row-order.js';
|
|
138
139
|
export { extent, linear } from './utils/scale/scale.js';
|
|
139
140
|
export { ticks } from './utils/scale/ticks.js';
|
|
140
|
-
export { scrollItemIntoView, scrollingAncestor, scrollRoom, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
|
|
141
|
+
export { scrollItemIntoView, scrollingAncestor, scrollRoom, scrollWithin, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
|
|
141
142
|
export { lockScroll, scrollHolders, unlockScroll } from './utils/scroll-lock/scroll-lock.js';
|
|
142
143
|
export { slotFilled, slotNamed } from './utils/slot-filled/slot-filled.js';
|
|
143
144
|
export { SlottedGlyph } from './utils/slotted-glyph/slotted-glyph.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { html, nothing } from 'lit';
|
|
3
3
|
import { property, state } from 'lit/decorators.js';
|
|
4
|
-
import {
|
|
4
|
+
import { auditField } from '@fusion.dev/helix-core/audits/name';
|
|
5
5
|
import { hostAria } from '../../utils/host-aria/host-aria.js';
|
|
6
6
|
const HINT_ID = 'helix-hint';
|
|
7
7
|
const FEEDBACK_ID = 'helix-feedback';
|
|
@@ -170,8 +170,7 @@ export const MixinField = (Base) => {
|
|
|
170
170
|
});
|
|
171
171
|
this.addEventListener('change', touch);
|
|
172
172
|
this.renderRoot.addEventListener('slotchange', this.noteSlotted);
|
|
173
|
-
|
|
174
|
-
[...this.children].some((child) => child.slot === 'label' && (child.textContent ?? '').trim() !== ''), () => this.partNamed('value'));
|
|
173
|
+
auditField(this);
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
176
|
__decorate([
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { auditName as audit } from './audit-name.js';
|
|
1
|
+
import type { auditName as audit, auditField as field, auditIconControl as iconControl } from './audit-name.js';
|
|
2
2
|
/**
|
|
3
3
|
* The missing-name warning in a production build: nothing.
|
|
4
4
|
*
|
|
@@ -8,3 +8,7 @@ import type { auditName as audit } from './audit-name.js';
|
|
|
8
8
|
* builds. See `audit-name.ts`.
|
|
9
9
|
*/
|
|
10
10
|
export declare const auditName: typeof audit;
|
|
11
|
+
/** The field's missing-name warning in a production build: nothing. */
|
|
12
|
+
export declare const auditField: typeof field;
|
|
13
|
+
/** The warning about a control named by a decorative icon, in a production build: nothing. */
|
|
14
|
+
export declare const auditIconControl: typeof iconControl;
|
|
@@ -48,3 +48,22 @@ export declare const hasName: (element: HTMLElement, shown?: Element | null) =>
|
|
|
48
48
|
* placeholder, whose words are no name.
|
|
49
49
|
*/
|
|
50
50
|
export declare const auditName: (host: HTMLElement, control: () => HTMLElement | null | undefined, fix: string, also?: () => boolean, shown?: () => Element | null) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Asks whether a field is named, as `auditName` does, with what every field
|
|
53
|
+
* answers it by: the control its label points at, a label it was given or one
|
|
54
|
+
* slotted with words in it, and the `value` part, whose words are no name.
|
|
55
|
+
*
|
|
56
|
+
* **Here rather than at the call in `MixinField`**, for the reason `FIXES` is
|
|
57
|
+
* here: the call is a no-op in production, and the closures it was handed were
|
|
58
|
+
* in every field's bundle anyway.
|
|
59
|
+
*/
|
|
60
|
+
export declare const auditField: (host: HTMLElement) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Asks the button or link a decorative icon sits in whether it has a name.
|
|
63
|
+
* Its content is the icon alone where it has none, and the warning says so.
|
|
64
|
+
* `label` is the icon's own, which names it, and the control with it.
|
|
65
|
+
*
|
|
66
|
+
* Words or a named picture beside the icon name the control from its
|
|
67
|
+
* content, which the audit reads for a button and not for a link.
|
|
68
|
+
*/
|
|
69
|
+
export declare const auditIconControl: (icon: HTMLElement, label: string) => void;
|
|
@@ -50,7 +50,37 @@ const implicitRole = (element) => {
|
|
|
50
50
|
return element.localName === 'a' && element.hasAttribute('href') ? 'link' : '';
|
|
51
51
|
};
|
|
52
52
|
const FIXES = new Map([
|
|
53
|
+
['accordion-item', 'Write its title in the `summary` slot.'],
|
|
53
54
|
['button', 'Give it words, a `label`, or an `aria-label`.'],
|
|
55
|
+
['button-group', 'Give it a `label`, or an `aria-label` that says what the buttons are for.'],
|
|
56
|
+
['canvas', 'Give it a `label` that says what the board holds, such as "Release plan".'],
|
|
57
|
+
['dialog', 'Give it a `label`, or a heading in the `header` slot.'],
|
|
58
|
+
[
|
|
59
|
+
'field',
|
|
60
|
+
'Give it a `label`, an `aria-label`, words in its label, or a `<label for>` that names it.',
|
|
61
|
+
],
|
|
62
|
+
['graph', 'Give it a `label` that says what the notes are, such as "Research notes".'],
|
|
63
|
+
['lightbox', 'Give it a `label`, such as "Project photos".'],
|
|
64
|
+
['list', 'Give it a `label`, or an `aria-label` that says what the rows are.'],
|
|
65
|
+
['map', 'Give it a `label` that says what the map shows, such as "Stores in Edinburgh".'],
|
|
66
|
+
['popover', 'Give it a `label`, or a heading in the panel.'],
|
|
67
|
+
['row', 'Give the row words in its default slot, or a label on the icon it holds.'],
|
|
68
|
+
['sandbox', 'Give each source pane a `label` or a `name`, which its file tab shows.'],
|
|
69
|
+
[
|
|
70
|
+
'table',
|
|
71
|
+
'Give it a `label`, or put a heading in its `heading` slot, that says what the rows are.',
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
'time-picker',
|
|
75
|
+
'Give it a `label`, a label in the `label` slot, or an `aria-label` that says what the time is for.',
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
'toggle',
|
|
79
|
+
'Give it words in its default slot, an `aria-label`, or a label on the icon it holds.',
|
|
80
|
+
],
|
|
81
|
+
['tour', 'Give it a `label`, such as "Getting started".'],
|
|
82
|
+
['tree', 'Give it a `label` that says what it holds, such as "Files".'],
|
|
83
|
+
['video', 'Give it a `label` that says what the video shows, such as "Product tour".'],
|
|
54
84
|
['virtual-list', 'Give it a `label`, or an `aria-label` that says what the items are.'],
|
|
55
85
|
]);
|
|
56
86
|
const audited = new WeakSet();
|
|
@@ -93,3 +123,18 @@ export const auditName = (host, control, fix, also, shown) => {
|
|
|
93
123
|
});
|
|
94
124
|
});
|
|
95
125
|
};
|
|
126
|
+
export const auditField = (host) => {
|
|
127
|
+
const field = host;
|
|
128
|
+
auditName(field, () => field.namedControl, 'field', () => field.labeled ||
|
|
129
|
+
[...field.children].some((child) => child.slot === 'label' && (child.textContent ?? '').trim() !== ''), () => field.partNamed('value'));
|
|
130
|
+
};
|
|
131
|
+
export const auditIconControl = (icon, label) => {
|
|
132
|
+
if (label !== '' || icon.hasAttribute('aria-label')) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const control = icon.closest('a[href], button, summary, [role="button"], [role="link"]');
|
|
136
|
+
if (control) {
|
|
137
|
+
auditName(control, () => null, `Its only content is <${icon.localName}>, which is decorative: give the icon a \`label\`, or the control an \`aria-label\`.`, () => (control.textContent ?? '').trim() !== '' ||
|
|
138
|
+
control.querySelector('[alt]:not([alt=""]), [aria-label]:not([aria-label=""])') !== null);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { x } from '@fusion.dev/helix-icons';
|
|
2
2
|
import { html, nothing } from 'lit';
|
|
3
|
-
import {
|
|
3
|
+
import { glyphPath } from '../glyph-path/glyph-path.js';
|
|
4
4
|
export const drawDismiss = ({ disabled = false, label, mark, onDismiss, tabindex, }) => html `<button
|
|
5
5
|
part="dismiss"
|
|
6
6
|
type="button"
|
|
@@ -12,5 +12,5 @@ export const drawDismiss = ({ disabled = false, label, mark, onDismiss, tabindex
|
|
|
12
12
|
${mark}
|
|
13
13
|
</button>`;
|
|
14
14
|
export const drawDismissGlyph = () => html `<svg part="dismiss-mark" viewBox=${x.viewBox} fill="currentColor" aria-hidden="true">
|
|
15
|
-
|
|
15
|
+
<path d=${glyphPath(x)}></path>
|
|
16
16
|
</svg>`;
|
|
@@ -11,7 +11,7 @@ export interface DrawIconOptions {
|
|
|
11
11
|
* Draws an icon that was imported by name, rather than looked up by string.
|
|
12
12
|
*
|
|
13
13
|
* **`<helix-icon name="play">` looks its glyph up in a registry at runtime.**
|
|
14
|
-
* The registry holds the whole set once `@fusion.dev/helix-headless/icon
|
|
14
|
+
* The registry holds the whole set once `@fusion.dev/helix-headless/icon`
|
|
15
15
|
* registers it, and no bundler can shake a lookup by string, so every consumer
|
|
16
16
|
* of any component that renders one ships all of the generated path data: measured at 50,814 bytes raw and 15,010 gzipped for 102 icons, and
|
|
17
17
|
* growing about 147 gzipped bytes per icon added. A named import ships one path
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `d` of a glyph drawn as one path, for a template that writes the
|
|
3
|
+
* `<path>` itself.
|
|
4
|
+
*
|
|
5
|
+
* **For a fixed glyph, which needs no directive.** `unsafeSVG(glyph.content)`
|
|
6
|
+
* parses markup a template could have written, and the directive and its base
|
|
7
|
+
* class come to about 1,000 bytes in a bundle that draws nothing else through
|
|
8
|
+
* it. Measured October 8, 2026, bundled alone, brotli: Dialog and Tag were
|
|
9
|
+
* each about 300 bytes larger for the cross alone, and Tabs about 350 for the
|
|
10
|
+
* cross and its four carets.
|
|
11
|
+
*
|
|
12
|
+
* A glyph of more than one element keeps `unsafeSVG`, which is what Icon draws
|
|
13
|
+
* every glyph with. `glyph-path.pure.spec.ts` holds each glyph drawn this way
|
|
14
|
+
* to a single path, so a set that redraws one in two cannot lose half of it.
|
|
15
|
+
*/
|
|
16
|
+
export declare const glyphPath: (glyph: {
|
|
17
|
+
readonly content: string;
|
|
18
|
+
}) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const glyphPath = (glyph) => /\sd="([^"]*)"/.exec(glyph.content)?.[1] ?? '';
|
|
@@ -58,3 +58,12 @@ export interface ScrollerOptions {
|
|
|
58
58
|
readonly stop?: Element | null;
|
|
59
59
|
}
|
|
60
60
|
export declare const scrollItemIntoView: (item: HTMLElement, options?: ScrollItemOptions) => void;
|
|
61
|
+
/**
|
|
62
|
+
* `scrollItemIntoView` in a scroller the caller already holds.
|
|
63
|
+
*
|
|
64
|
+
* **For a family that always knows its scroller**, as Tabs knows its strip:
|
|
65
|
+
* `scrollingAncestor` and the walk it takes are then in no bundle of its own,
|
|
66
|
+
* where the fallback carried them into every one. Measured October 8, 2026,
|
|
67
|
+
* bundled alone, brotli: about 165 bytes on Tabs.
|
|
68
|
+
*/
|
|
69
|
+
export declare const scrollWithin: (item: HTMLElement, scroller: HTMLElement, { padding, smooth }?: Omit<ScrollItemOptions, 'scroller'>) => void;
|
|
@@ -29,11 +29,12 @@ const climb = (node) => {
|
|
|
29
29
|
return root instanceof ShadowRoot ? root.host : null;
|
|
30
30
|
};
|
|
31
31
|
export const scrollItemIntoView = (item, options = {}) => {
|
|
32
|
-
const { padding = 0, smooth = false } = options;
|
|
33
32
|
const scroller = options.scroller ?? scrollingAncestor(item);
|
|
34
|
-
if (
|
|
35
|
-
|
|
33
|
+
if (scroller) {
|
|
34
|
+
scrollWithin(item, scroller, options);
|
|
36
35
|
}
|
|
36
|
+
};
|
|
37
|
+
export const scrollWithin = (item, scroller, { padding = 0, smooth = false } = {}) => {
|
|
37
38
|
const box = item.getBoundingClientRect();
|
|
38
39
|
const view = scroller.getBoundingClientRect();
|
|
39
40
|
const before = { block: view.top - box.top + padding, inline: view.left - box.left + padding };
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.186.0';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix-core",
|
|
3
3
|
"description": "Helix element primitives: base classes, mixins, decorators, utilities.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.186.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@floating-ui/dom": "^1.8.0",
|
|
77
|
-
"@fusion.dev/helix-icons": "0.
|
|
77
|
+
"@fusion.dev/helix-icons": "0.186.0",
|
|
78
78
|
"tslib": "^2.8.1"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@fusion.dev/helix-cem": "0.
|
|
82
|
-
"@fusion.dev/helix-testing": "0.
|
|
81
|
+
"@fusion.dev/helix-cem": "0.186.0",
|
|
82
|
+
"@fusion.dev/helix-testing": "0.186.0",
|
|
83
83
|
"@lit-labs/ssr": "^4.1.0",
|
|
84
84
|
"lit": "^3.2.1"
|
|
85
85
|
},
|