@timmo001/groundwork-ui 0.9.3 → 0.9.5
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 +98 -3
- package/package.json +2 -2
- package/src/gw-app-bar.ts +5 -1
- package/src/gw-badge.ts +1 -1
- package/src/gw-banner.ts +1 -1
- package/src/gw-card.ts +6 -2
- package/src/gw-chip.ts +1 -1
- package/src/gw-command-menu.ts +1 -1
- package/src/gw-expansion.ts +1 -1
- package/src/gw-menu-button.ts +131 -9
- package/src/gw-section-title.ts +5 -1
- package/src/gw-setting-row.ts +1 -1
package/custom-elements.json
CHANGED
|
@@ -1628,6 +1628,9 @@
|
|
|
1628
1628
|
"kind": "field",
|
|
1629
1629
|
"name": "#nav",
|
|
1630
1630
|
"privacy": "private",
|
|
1631
|
+
"type": {
|
|
1632
|
+
"text": "HTMLDivElement"
|
|
1633
|
+
},
|
|
1631
1634
|
"readonly": true
|
|
1632
1635
|
},
|
|
1633
1636
|
{
|
|
@@ -2228,6 +2231,9 @@
|
|
|
2228
2231
|
"kind": "field",
|
|
2229
2232
|
"name": "#value",
|
|
2230
2233
|
"privacy": "private",
|
|
2234
|
+
"type": {
|
|
2235
|
+
"text": "HTMLSpanElement"
|
|
2236
|
+
},
|
|
2231
2237
|
"readonly": true
|
|
2232
2238
|
},
|
|
2233
2239
|
{
|
|
@@ -2696,6 +2702,9 @@
|
|
|
2696
2702
|
"kind": "field",
|
|
2697
2703
|
"name": "#bar",
|
|
2698
2704
|
"privacy": "private",
|
|
2705
|
+
"type": {
|
|
2706
|
+
"text": "HTMLDivElement"
|
|
2707
|
+
},
|
|
2699
2708
|
"readonly": true
|
|
2700
2709
|
},
|
|
2701
2710
|
{
|
|
@@ -3816,6 +3825,9 @@
|
|
|
3816
3825
|
"kind": "field",
|
|
3817
3826
|
"name": "#header",
|
|
3818
3827
|
"privacy": "private",
|
|
3828
|
+
"type": {
|
|
3829
|
+
"text": "HTMLElement"
|
|
3830
|
+
},
|
|
3819
3831
|
"readonly": true
|
|
3820
3832
|
},
|
|
3821
3833
|
{
|
|
@@ -3828,6 +3840,9 @@
|
|
|
3828
3840
|
"kind": "field",
|
|
3829
3841
|
"name": "#body",
|
|
3830
3842
|
"privacy": "private",
|
|
3843
|
+
"type": {
|
|
3844
|
+
"text": "HTMLDivElement"
|
|
3845
|
+
},
|
|
3831
3846
|
"readonly": true
|
|
3832
3847
|
},
|
|
3833
3848
|
{
|
|
@@ -5461,6 +5476,9 @@
|
|
|
5461
5476
|
"kind": "field",
|
|
5462
5477
|
"name": "#content",
|
|
5463
5478
|
"privacy": "private",
|
|
5479
|
+
"type": {
|
|
5480
|
+
"text": "HTMLSpanElement"
|
|
5481
|
+
},
|
|
5464
5482
|
"readonly": true
|
|
5465
5483
|
},
|
|
5466
5484
|
{
|
|
@@ -5932,6 +5950,9 @@
|
|
|
5932
5950
|
"kind": "field",
|
|
5933
5951
|
"name": "#dialog",
|
|
5934
5952
|
"privacy": "private",
|
|
5953
|
+
"type": {
|
|
5954
|
+
"text": "HTMLDialogElement"
|
|
5955
|
+
},
|
|
5935
5956
|
"readonly": true
|
|
5936
5957
|
},
|
|
5937
5958
|
{
|
|
@@ -7809,6 +7830,9 @@
|
|
|
7809
7830
|
"kind": "field",
|
|
7810
7831
|
"name": "#details",
|
|
7811
7832
|
"privacy": "private",
|
|
7833
|
+
"type": {
|
|
7834
|
+
"text": "HTMLDetailsElement"
|
|
7835
|
+
},
|
|
7812
7836
|
"readonly": true
|
|
7813
7837
|
},
|
|
7814
7838
|
{
|
|
@@ -10318,7 +10342,7 @@
|
|
|
10318
10342
|
"declarations": [
|
|
10319
10343
|
{
|
|
10320
10344
|
"kind": "class",
|
|
10321
|
-
"description": "A button that opens a menu on the Popover API, so it shows in the top\nlayer and closes on Esc or a click outside. Set `items`; arrow keys, Home\nand End move between them, and choosing one fires `select` with its\n`value` and closes the menu.\n\nContextual actions, such as \"Add a schedule\", go in `actions`: they stay\nbelow the items as the list scrolls, so they're found without scrolling to\nthe end, and with no items the menu says `empty` above them.\n\nItems render inside the component, so a page's own button styles can't\nreach them.",
|
|
10345
|
+
"description": "A button that opens a menu on the Popover API, so it shows in the top\nlayer and closes on Esc or a click outside. Set `items`; arrow keys, Home\nand End move between them, and choosing one fires `select` with its\n`value` and closes the menu.\n\nContextual actions, such as \"Add a schedule\", go in `actions`: they stay\nbelow the items as the list scrolls, so they're found without scrolling to\nthe end, and with no items the menu says `empty` above them.\n\nFor a menu of choices, such as sort and order, give each item `checked`:\nthe picked one shows a tick, and `separator` starts the next group.\n\nItems render inside the component, so a page's own button styles can't\nreach them.",
|
|
10322
10346
|
"name": "GwMenuButton",
|
|
10323
10347
|
"cssProperties": [
|
|
10324
10348
|
{
|
|
@@ -10344,7 +10368,7 @@
|
|
|
10344
10368
|
"text": "Properties"
|
|
10345
10369
|
},
|
|
10346
10370
|
"static": true,
|
|
10347
|
-
"default": "{ label: {}, icon: {}, empty: {}, items: { type: \"json\" }, actions: { type: \"json\" }, size: { reflect: true }, }",
|
|
10371
|
+
"default": "{ label: {}, heading: {}, icon: {}, empty: {}, items: { type: \"json\" }, actions: { type: \"json\" }, size: { reflect: true }, active: { type: \"boolean\", reflect: true }, labels: { reflect: true }, }",
|
|
10348
10372
|
"inheritedFrom": {
|
|
10349
10373
|
"name": "GwElement",
|
|
10350
10374
|
"module": "src/element.ts"
|
|
@@ -10357,7 +10381,7 @@
|
|
|
10357
10381
|
"text": "readonly CSSStyleSheet[]"
|
|
10358
10382
|
},
|
|
10359
10383
|
"static": true,
|
|
10360
|
-
"default": "[ base, controlSizes(\"menu-button\"), css` :host { position: relative; display: inline-block; anchor-scope: --gw-menu; } .trigger { --gw-size: var(--gw-menu-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.25rem * var(--gw-scale)); display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; box-sizing: border-box; height: calc(2.5rem * var(--gw-scale)); padding: 0 calc(var(--gw-space-4) * var(--gw-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-size)); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); cursor: pointer; background: var(--gw-color-fill-neutral-resting); border: var(--gw-border-width) solid var(--gw-border-color); border-radius: var(--gw-radius-control); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); anchor-name: --gw-menu; } .trigger:hover { background: var(--gw-color-fill-neutral-hovered); } .chevron { width: 0.4rem; height: 0.4rem; margin-inline-start: var(--gw-space-1); border-inline-end: 1.5px solid currentColor; border-block-end: 1.5px solid currentColor; rotate: 45deg; translate: 0 -0.125rem; transition: rotate var(--gw-motion-normal) var(--gw-motion-easing), translate var(--gw-motion-normal) var(--gw-motion-easing); } .trigger[aria-expanded=\"true\"] .chevron { rotate: 225deg; translate: 0 0.125rem; } [popover] { position-anchor: --gw-menu; position-try-fallbacks: flip-block, flip-inline; inset: auto; top: anchor(bottom); left: anchor(left); box-sizing: border-box; min-width: 15rem; max-height: min(24rem, 60vh); padding: 0; margin: var(--gw-space-1) 0 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-l); box-shadow: var(--gw-shadow-m); opacity: 0; translate: 0 -0.25rem; transition: opacity var(--gw-motion-fast) var(--gw-motion-easing), translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; flex-direction: column; opacity: 1; translate: 0 0; } @starting-style { [popover]:popover-open { opacity: 0; translate: 0 -0.25rem; } } /* The items scroll; the actions stay put below them, so \"Add\" is always in reach rather than at the end of a long list */ .items, .actions { display: flex; flex-direction: column; gap: var(--gw-space-0-5); padding: var(--gw-space-1); } .items { flex: 1; min-height: 0; overflow-y: auto; } .actions { border-block-start: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); } .empty { padding: var(--gw-space-3); margin: 0; font-size: var(--gw-font-size-s); color: var(--gw-color-text-secondary); } .item { display: flex; gap: var(--gw-space-3); align-items: center; box-sizing: border-box; min-height: 2.5rem; padding: var(--gw-space-2) var(--gw-space-3); font: inherit; font-size: var(--gw-font-size-s); color: var(--gw-color-text-primary); text-align: start; cursor: pointer; background: transparent; border: 0; border-radius: var(--gw-radius-m); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); --gw-icon-size: 1.125rem; } .item:hover, .item:focus-visible { background: var(--gw-color-fill-neutral-hovered); outline: none; } .item:focus-visible { box-shadow: inset 0 0 0 var(--gw-focus-width) var(--gw-color-focus); } .item:disabled { color: var(--gw-color-text-disabled); cursor: not-allowed; background: transparent; } .action { font-weight: var(--gw-font-weight-strong); color: var(--gw-color-accent); } .text { display: flex; flex-direction: column; gap: var(--gw-space-0-5); } .description { font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } `, ]",
|
|
10384
|
+
"default": "[ base, controlSizes(\"menu-button\"), css` :host { position: relative; display: inline-block; anchor-scope: --gw-menu; } .trigger { --gw-size: var(--gw-menu-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.25rem * var(--gw-scale)); display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; box-sizing: border-box; height: calc(2.5rem * var(--gw-scale)); padding: 0 calc(var(--gw-space-4) * var(--gw-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-size)); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); cursor: pointer; background: var(--gw-color-fill-neutral-resting); border: var(--gw-border-width) solid var(--gw-border-color); border-radius: var(--gw-radius-control); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); anchor-name: --gw-menu; } .trigger:hover { background: var(--gw-color-fill-neutral-hovered); } :host([active]) .trigger { color: var(--gw-color-accent); border-color: var(--gw-color-accent); } /* Icon only, square, with the text kept for assistive technology */ :host([labels=\"hide\"]) .trigger.has-icon { padding: 0 calc(var(--gw-space-2) * var(--gw-size)); } :host([labels=\"hide\"]) .has-icon .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @media (width < 40rem) { :host([labels=\"auto\"]) .trigger.has-icon { padding: 0 calc(var(--gw-space-2) * var(--gw-size)); } :host([labels=\"auto\"]) .has-icon .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } } .chevron { width: 0.4rem; height: 0.4rem; margin-inline-start: var(--gw-space-1); border-inline-end: 1.5px solid currentColor; border-block-end: 1.5px solid currentColor; rotate: 45deg; translate: 0 -0.125rem; transition: rotate var(--gw-motion-normal) var(--gw-motion-easing), translate var(--gw-motion-normal) var(--gw-motion-easing); } .trigger[aria-expanded=\"true\"] .chevron { rotate: 225deg; translate: 0 0.125rem; } [popover] { position-anchor: --gw-menu; position-try-fallbacks: flip-block, flip-inline; inset: auto; top: anchor(bottom); left: anchor(left); box-sizing: border-box; min-width: 15rem; max-height: min(24rem, 60vh); padding: 0; margin: var(--gw-space-1) 0 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-l); box-shadow: var(--gw-shadow-m); opacity: 0; translate: 0 -0.25rem; transition: opacity var(--gw-motion-fast) var(--gw-motion-easing), translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; flex-direction: column; opacity: 1; translate: 0 0; } @starting-style { [popover]:popover-open { opacity: 0; translate: 0 -0.25rem; } } /* The items scroll; the actions stay put below them, so \"Add\" is always in reach rather than at the end of a long list */ .items, .actions { display: flex; flex-direction: column; gap: var(--gw-space-0-5); padding: var(--gw-space-1); } .items { flex: 1; min-height: 0; overflow-y: auto; } .actions { border-block-start: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); } .empty { padding: var(--gw-space-3); margin: 0; font-size: var(--gw-font-size-s); color: var(--gw-color-text-secondary); } .item { display: flex; gap: var(--gw-space-3); align-items: center; box-sizing: border-box; min-height: 2.5rem; padding: var(--gw-space-2) var(--gw-space-3); font: inherit; font-size: var(--gw-font-size-s); color: var(--gw-color-text-primary); text-align: start; cursor: pointer; background: transparent; border: 0; border-radius: var(--gw-radius-m); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); --gw-icon-size: 1.125rem; } .item:hover, .item:focus-visible { background: var(--gw-color-fill-neutral-hovered); outline: none; } .item:focus-visible { box-shadow: inset 0 0 0 var(--gw-focus-width) var(--gw-color-focus); } .item:disabled { color: var(--gw-color-text-disabled); cursor: not-allowed; background: transparent; } .action { font-weight: var(--gw-font-weight-strong); color: var(--gw-color-accent); } /* Holds the tick's place on every choice, so the labels line up */ .tick { flex: none; width: var(--gw-icon-size); height: var(--gw-icon-size); color: var(--gw-color-accent); } .item[aria-checked=\"false\"] .tick { visibility: hidden; } .separator { flex: none; height: 0; margin: var(--gw-space-0-5) calc(-1 * var(--gw-space-1)); border: 0; border-block-start: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); } .text { display: flex; flex-direction: column; gap: var(--gw-space-0-5); } .description { font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } `, ]",
|
|
10361
10385
|
"inheritedFrom": {
|
|
10362
10386
|
"name": "GwElement",
|
|
10363
10387
|
"module": "src/element.ts"
|
|
@@ -10371,6 +10395,15 @@
|
|
|
10371
10395
|
},
|
|
10372
10396
|
"default": "\"\""
|
|
10373
10397
|
},
|
|
10398
|
+
{
|
|
10399
|
+
"kind": "field",
|
|
10400
|
+
"name": "heading",
|
|
10401
|
+
"type": {
|
|
10402
|
+
"text": "string"
|
|
10403
|
+
},
|
|
10404
|
+
"description": "What the menu is for, such as \"Sort by\", when the trigger shows the current choice.",
|
|
10405
|
+
"default": "\"\""
|
|
10406
|
+
},
|
|
10374
10407
|
{
|
|
10375
10408
|
"kind": "field",
|
|
10376
10409
|
"name": "icon",
|
|
@@ -10413,6 +10446,24 @@
|
|
|
10413
10446
|
},
|
|
10414
10447
|
"default": "\"medium\""
|
|
10415
10448
|
},
|
|
10449
|
+
{
|
|
10450
|
+
"kind": "field",
|
|
10451
|
+
"name": "active",
|
|
10452
|
+
"type": {
|
|
10453
|
+
"text": "boolean"
|
|
10454
|
+
},
|
|
10455
|
+
"description": "Marks the trigger as set, such as a grouping that's on.",
|
|
10456
|
+
"default": "false"
|
|
10457
|
+
},
|
|
10458
|
+
{
|
|
10459
|
+
"kind": "field",
|
|
10460
|
+
"name": "labels",
|
|
10461
|
+
"type": {
|
|
10462
|
+
"text": "\"show\" | \"hide\" | \"auto\""
|
|
10463
|
+
},
|
|
10464
|
+
"description": "Whether the trigger's text shows beside its icon: always, never, or not on narrow screens.",
|
|
10465
|
+
"default": "\"show\""
|
|
10466
|
+
},
|
|
10416
10467
|
{
|
|
10417
10468
|
"kind": "field",
|
|
10418
10469
|
"name": "#trigger",
|
|
@@ -10423,6 +10474,18 @@
|
|
|
10423
10474
|
"kind": "field",
|
|
10424
10475
|
"name": "#triggerText",
|
|
10425
10476
|
"privacy": "private",
|
|
10477
|
+
"type": {
|
|
10478
|
+
"text": "HTMLSpanElement"
|
|
10479
|
+
},
|
|
10480
|
+
"readonly": true
|
|
10481
|
+
},
|
|
10482
|
+
{
|
|
10483
|
+
"kind": "field",
|
|
10484
|
+
"name": "#heading",
|
|
10485
|
+
"privacy": "private",
|
|
10486
|
+
"type": {
|
|
10487
|
+
"text": "HTMLSpanElement"
|
|
10488
|
+
},
|
|
10426
10489
|
"readonly": true
|
|
10427
10490
|
},
|
|
10428
10491
|
{
|
|
@@ -10500,6 +10563,11 @@
|
|
|
10500
10563
|
}
|
|
10501
10564
|
]
|
|
10502
10565
|
},
|
|
10566
|
+
{
|
|
10567
|
+
"kind": "method",
|
|
10568
|
+
"name": "#tick",
|
|
10569
|
+
"privacy": "private"
|
|
10570
|
+
},
|
|
10503
10571
|
{
|
|
10504
10572
|
"kind": "method",
|
|
10505
10573
|
"name": "create",
|
|
@@ -10829,6 +10897,13 @@
|
|
|
10829
10897
|
"description": "The trigger's text.",
|
|
10830
10898
|
"name": "label"
|
|
10831
10899
|
},
|
|
10900
|
+
{
|
|
10901
|
+
"type": {
|
|
10902
|
+
"text": "string"
|
|
10903
|
+
},
|
|
10904
|
+
"description": "What the menu is for, such as \"Sort by\", when the trigger shows the current choice: it names the menu, starts the trigger's accessible name and is its tooltip.",
|
|
10905
|
+
"name": "heading"
|
|
10906
|
+
},
|
|
10832
10907
|
{
|
|
10833
10908
|
"type": {
|
|
10834
10909
|
"text": "string"
|
|
@@ -10863,6 +10938,20 @@
|
|
|
10863
10938
|
},
|
|
10864
10939
|
"description": "The trigger's size, 24 to 56px tall. Defaults to `medium`.",
|
|
10865
10940
|
"name": "size"
|
|
10941
|
+
},
|
|
10942
|
+
{
|
|
10943
|
+
"type": {
|
|
10944
|
+
"text": "boolean"
|
|
10945
|
+
},
|
|
10946
|
+
"description": "Marks the trigger as set, such as a grouping that's on, with an accent border and text.",
|
|
10947
|
+
"name": "active"
|
|
10948
|
+
},
|
|
10949
|
+
{
|
|
10950
|
+
"type": {
|
|
10951
|
+
"text": "\"show\"|\"hide\"|\"auto\""
|
|
10952
|
+
},
|
|
10953
|
+
"description": "Whether the trigger's text shows beside its icon: always, never, or not on narrow screens. Defaults to `show`.",
|
|
10954
|
+
"name": "labels"
|
|
10866
10955
|
}
|
|
10867
10956
|
],
|
|
10868
10957
|
"superclass": {
|
|
@@ -14260,6 +14349,9 @@
|
|
|
14260
14349
|
"kind": "field",
|
|
14261
14350
|
"name": "#heading",
|
|
14262
14351
|
"privacy": "private",
|
|
14352
|
+
"type": {
|
|
14353
|
+
"text": "HTMLDivElement"
|
|
14354
|
+
},
|
|
14263
14355
|
"readonly": true
|
|
14264
14356
|
},
|
|
14265
14357
|
{
|
|
@@ -16014,6 +16106,9 @@
|
|
|
16014
16106
|
"kind": "field",
|
|
16015
16107
|
"name": "#descriptionSkeleton",
|
|
16016
16108
|
"privacy": "private",
|
|
16109
|
+
"type": {
|
|
16110
|
+
"text": "HTMLDivElement"
|
|
16111
|
+
},
|
|
16017
16112
|
"readonly": true
|
|
16018
16113
|
},
|
|
16019
16114
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timmo001/groundwork-ui",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Groundwork's framework-free web components, styled by the --gw-* tokens",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"scripts": {
|
|
29
29
|
"check": "bun run --parallel --no-exit-on-error lint typecheck format:check",
|
|
30
30
|
"lint": "oxlint .",
|
|
31
|
-
"typecheck": "tsc",
|
|
31
|
+
"typecheck": "tsc && bun run scripts/typecheck-alone.ts",
|
|
32
32
|
"format": "prettier --write .",
|
|
33
33
|
"format:check": "prettier --check .",
|
|
34
34
|
"gen": "bun run scripts/manifest.ts",
|
package/src/gw-app-bar.ts
CHANGED
|
@@ -237,7 +237,11 @@ export class GwAppBar extends GwElement {
|
|
|
237
237
|
this.#menuIcon,
|
|
238
238
|
);
|
|
239
239
|
|
|
240
|
-
readonly #nav
|
|
240
|
+
readonly #nav: HTMLDivElement = h(
|
|
241
|
+
"div",
|
|
242
|
+
{ class: "nav", part: "nav" },
|
|
243
|
+
h("slot"),
|
|
244
|
+
);
|
|
241
245
|
|
|
242
246
|
#observer: IntersectionObserver | undefined;
|
|
243
247
|
|
package/src/gw-badge.ts
CHANGED
|
@@ -134,7 +134,7 @@ export class GwBadge extends GwElement {
|
|
|
134
134
|
style: "--gw-skeleton-width: 3rem",
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
-
readonly #value = h("span", { class: "value" }, h("slot"));
|
|
137
|
+
readonly #value: HTMLSpanElement = h("span", { class: "value" }, h("slot"));
|
|
138
138
|
|
|
139
139
|
constructor() {
|
|
140
140
|
super();
|
package/src/gw-banner.ts
CHANGED
package/src/gw-card.ts
CHANGED
|
@@ -139,7 +139,7 @@ export class GwCard extends GwElement {
|
|
|
139
139
|
|
|
140
140
|
readonly #headingSlot = h("slot", { name: "heading" });
|
|
141
141
|
|
|
142
|
-
readonly #header = h(
|
|
142
|
+
readonly #header: HTMLElement = h(
|
|
143
143
|
"header",
|
|
144
144
|
{ part: "header" },
|
|
145
145
|
h(
|
|
@@ -154,7 +154,11 @@ export class GwCard extends GwElement {
|
|
|
154
154
|
|
|
155
155
|
readonly #skeleton = h("gw-skeleton-lines");
|
|
156
156
|
|
|
157
|
-
readonly #body
|
|
157
|
+
readonly #body: HTMLDivElement = h(
|
|
158
|
+
"div",
|
|
159
|
+
{ class: "body", part: "body" },
|
|
160
|
+
h("slot"),
|
|
161
|
+
);
|
|
158
162
|
|
|
159
163
|
#card: HTMLElement = h("section", { class: "card", part: "card" });
|
|
160
164
|
|
package/src/gw-chip.ts
CHANGED
|
@@ -115,7 +115,7 @@ export class GwChip extends GwElement {
|
|
|
115
115
|
style: "--gw-skeleton-width: 3.5rem",
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
readonly #content = h(
|
|
118
|
+
readonly #content: HTMLSpanElement = h(
|
|
119
119
|
"span",
|
|
120
120
|
{ class: "content" },
|
|
121
121
|
h("slot", { name: "icon" }, this.#icon, this.#swatch),
|
package/src/gw-command-menu.ts
CHANGED
|
@@ -156,7 +156,7 @@ export class GwCommandMenu extends GwElement {
|
|
|
156
156
|
|
|
157
157
|
readonly #list = h("gw-option-list", { search: true, part: "list" });
|
|
158
158
|
|
|
159
|
-
readonly #dialog = h(
|
|
159
|
+
readonly #dialog: HTMLDialogElement = h(
|
|
160
160
|
"dialog",
|
|
161
161
|
{ part: "dialog" },
|
|
162
162
|
this.#list,
|
package/src/gw-expansion.ts
CHANGED
package/src/gw-menu-button.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
-
import { GwElement, css, h } from "./element.js";
|
|
7
|
+
import { GwElement, css, h, setAttribute } from "./element.js";
|
|
8
8
|
import { base, controlSizes, createIcon, setIconPath } from "./styles.js";
|
|
9
9
|
|
|
10
10
|
export interface MenuItem {
|
|
@@ -15,8 +15,18 @@ export interface MenuItem {
|
|
|
15
15
|
/** SVG path data for an icon before the label. */
|
|
16
16
|
readonly icon?: string;
|
|
17
17
|
readonly disabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the item is the one picked, for a menu of choices such as a sort
|
|
20
|
+
* order. Give it on every item in the group: the picked one shows a tick.
|
|
21
|
+
*/
|
|
22
|
+
readonly checked?: boolean;
|
|
23
|
+
/** Draws a line above the item, starting a new group. */
|
|
24
|
+
readonly separator?: boolean;
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
// mdi's check, for the picked item in a menu of choices
|
|
28
|
+
const tick = "M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z";
|
|
29
|
+
|
|
20
30
|
/**
|
|
21
31
|
* A button that opens a menu on the Popover API, so it shows in the top
|
|
22
32
|
* layer and closes on Esc or a click outside. Set `items`; arrow keys, Home
|
|
@@ -27,6 +37,9 @@ export interface MenuItem {
|
|
|
27
37
|
* below the items as the list scrolls, so they're found without scrolling to
|
|
28
38
|
* the end, and with no items the menu says `empty` above them.
|
|
29
39
|
*
|
|
40
|
+
* For a menu of choices, such as sort and order, give each item `checked`:
|
|
41
|
+
* the picked one shows a tick, and `separator` starts the next group.
|
|
42
|
+
*
|
|
30
43
|
* Items render inside the component, so a page's own button styles can't
|
|
31
44
|
* reach them.
|
|
32
45
|
*
|
|
@@ -34,11 +47,14 @@ export interface MenuItem {
|
|
|
34
47
|
* @summary A button that opens a menu of items and actions.
|
|
35
48
|
*
|
|
36
49
|
* @attr {string} label - The trigger's text.
|
|
50
|
+
* @attr {string} heading - What the menu is for, such as "Sort by", when the trigger shows the current choice: it names the menu, starts the trigger's accessible name and is its tooltip.
|
|
37
51
|
* @attr {string} icon - SVG path data for an icon before the trigger's text.
|
|
38
52
|
* @attr {string} empty - What the menu says with no items. Defaults to `Nothing here yet`.
|
|
39
53
|
* @attr {MenuItem[]} items - The items, as JSON.
|
|
40
54
|
* @attr {MenuItem[]} actions - Actions pinned under the items, as JSON.
|
|
41
55
|
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The trigger's size, 24 to 56px tall. Defaults to `medium`.
|
|
56
|
+
* @attr {boolean} active - Marks the trigger as set, such as a grouping that's on, with an accent border and text.
|
|
57
|
+
* @attr {"show"|"hide"|"auto"} labels - Whether the trigger's text shows beside its icon: always, never, or not on narrow screens. Defaults to `show`.
|
|
42
58
|
*
|
|
43
59
|
* @csspart trigger - The button that opens the menu.
|
|
44
60
|
*
|
|
@@ -50,11 +66,14 @@ export interface MenuItem {
|
|
|
50
66
|
export class GwMenuButton extends GwElement {
|
|
51
67
|
static override properties = {
|
|
52
68
|
label: {},
|
|
69
|
+
heading: {},
|
|
53
70
|
icon: {},
|
|
54
71
|
empty: {},
|
|
55
72
|
items: { type: "json" },
|
|
56
73
|
actions: { type: "json" },
|
|
57
74
|
size: { reflect: true },
|
|
75
|
+
active: { type: "boolean", reflect: true },
|
|
76
|
+
labels: { reflect: true },
|
|
58
77
|
} as const;
|
|
59
78
|
|
|
60
79
|
static override styles = [
|
|
@@ -95,6 +114,40 @@ export class GwMenuButton extends GwElement {
|
|
|
95
114
|
background: var(--gw-color-fill-neutral-hovered);
|
|
96
115
|
}
|
|
97
116
|
|
|
117
|
+
:host([active]) .trigger {
|
|
118
|
+
color: var(--gw-color-accent);
|
|
119
|
+
border-color: var(--gw-color-accent);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Icon only, square, with the text kept for assistive technology */
|
|
123
|
+
:host([labels="hide"]) .trigger.has-icon {
|
|
124
|
+
padding: 0 calc(var(--gw-space-2) * var(--gw-size));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:host([labels="hide"]) .has-icon .label {
|
|
128
|
+
position: absolute;
|
|
129
|
+
width: 1px;
|
|
130
|
+
height: 1px;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
clip-path: inset(50%);
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@media (width < 40rem) {
|
|
137
|
+
:host([labels="auto"]) .trigger.has-icon {
|
|
138
|
+
padding: 0 calc(var(--gw-space-2) * var(--gw-size));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
:host([labels="auto"]) .has-icon .label {
|
|
142
|
+
position: absolute;
|
|
143
|
+
width: 1px;
|
|
144
|
+
height: 1px;
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
clip-path: inset(50%);
|
|
147
|
+
white-space: nowrap;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
98
151
|
.chevron {
|
|
99
152
|
width: 0.4rem;
|
|
100
153
|
height: 0.4rem;
|
|
@@ -223,6 +276,27 @@ export class GwMenuButton extends GwElement {
|
|
|
223
276
|
color: var(--gw-color-accent);
|
|
224
277
|
}
|
|
225
278
|
|
|
279
|
+
/* Holds the tick's place on every choice, so the labels line up */
|
|
280
|
+
.tick {
|
|
281
|
+
flex: none;
|
|
282
|
+
width: var(--gw-icon-size);
|
|
283
|
+
height: var(--gw-icon-size);
|
|
284
|
+
color: var(--gw-color-accent);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.item[aria-checked="false"] .tick {
|
|
288
|
+
visibility: hidden;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.separator {
|
|
292
|
+
flex: none;
|
|
293
|
+
height: 0;
|
|
294
|
+
margin: var(--gw-space-0-5) calc(-1 * var(--gw-space-1));
|
|
295
|
+
border: 0;
|
|
296
|
+
border-block-start: max(var(--gw-border-width), 1px) solid
|
|
297
|
+
var(--gw-color-border-normal);
|
|
298
|
+
}
|
|
299
|
+
|
|
226
300
|
.text {
|
|
227
301
|
display: flex;
|
|
228
302
|
flex-direction: column;
|
|
@@ -237,6 +311,8 @@ export class GwMenuButton extends GwElement {
|
|
|
237
311
|
];
|
|
238
312
|
|
|
239
313
|
declare label: string;
|
|
314
|
+
/** What the menu is for, such as "Sort by", when the trigger shows the current choice. */
|
|
315
|
+
declare heading: string;
|
|
240
316
|
/** SVG path data for an icon before the trigger's label. */
|
|
241
317
|
declare icon: string;
|
|
242
318
|
/** What the menu says when it has no items, such as "No backups yet". */
|
|
@@ -244,6 +320,10 @@ export class GwMenuButton extends GwElement {
|
|
|
244
320
|
declare items: readonly MenuItem[];
|
|
245
321
|
declare actions: readonly MenuItem[];
|
|
246
322
|
declare size: "xs" | "small" | "medium" | "large" | "xl";
|
|
323
|
+
/** Marks the trigger as set, such as a grouping that's on. */
|
|
324
|
+
declare active: boolean;
|
|
325
|
+
/** Whether the trigger's text shows beside its icon: always, never, or not on narrow screens. */
|
|
326
|
+
declare labels: "show" | "hide" | "auto";
|
|
247
327
|
|
|
248
328
|
readonly #trigger = h("button", {
|
|
249
329
|
class: "trigger",
|
|
@@ -253,7 +333,16 @@ export class GwMenuButton extends GwElement {
|
|
|
253
333
|
"aria-expanded": "false",
|
|
254
334
|
});
|
|
255
335
|
|
|
256
|
-
readonly #triggerText =
|
|
336
|
+
readonly #triggerText: HTMLSpanElement = h("span", {
|
|
337
|
+
class: "label",
|
|
338
|
+
id: "label",
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Read before the label, for a trigger that shows the current choice
|
|
342
|
+
readonly #heading: HTMLSpanElement = h("span", {
|
|
343
|
+
id: "heading",
|
|
344
|
+
hidden: true,
|
|
345
|
+
});
|
|
257
346
|
|
|
258
347
|
readonly #triggerIcon = createIcon("");
|
|
259
348
|
|
|
@@ -271,11 +360,14 @@ export class GwMenuButton extends GwElement {
|
|
|
271
360
|
constructor() {
|
|
272
361
|
super();
|
|
273
362
|
this.label = "";
|
|
363
|
+
this.heading = "";
|
|
274
364
|
this.icon = "";
|
|
275
365
|
this.empty = "Nothing here yet";
|
|
276
366
|
this.items = [];
|
|
277
367
|
this.actions = [];
|
|
278
368
|
this.size = "medium";
|
|
369
|
+
this.active = false;
|
|
370
|
+
this.labels = "show";
|
|
279
371
|
}
|
|
280
372
|
|
|
281
373
|
/** The enabled items then actions, in the order arrow keys move through them. */
|
|
@@ -327,14 +419,18 @@ export class GwMenuButton extends GwElement {
|
|
|
327
419
|
}
|
|
328
420
|
|
|
329
421
|
#renderItem(item: MenuItem, kind: "item" | "action") {
|
|
422
|
+
const choice = item.checked !== undefined;
|
|
423
|
+
|
|
330
424
|
const button = h(
|
|
331
425
|
"button",
|
|
332
426
|
{
|
|
333
427
|
class: kind === "action" ? "item action" : "item",
|
|
334
|
-
role: "menuitem",
|
|
428
|
+
role: choice ? "menuitemradio" : "menuitem",
|
|
429
|
+
"aria-checked": choice ? String(item.checked) : null,
|
|
335
430
|
value: item.value,
|
|
336
431
|
disabled: item.disabled === true,
|
|
337
432
|
},
|
|
433
|
+
choice ? this.#tick() : null,
|
|
338
434
|
item.icon ? createIcon(item.icon) : null,
|
|
339
435
|
h(
|
|
340
436
|
"span",
|
|
@@ -348,7 +444,17 @@ export class GwMenuButton extends GwElement {
|
|
|
348
444
|
|
|
349
445
|
button.addEventListener("click", () => this.#choose(item.value));
|
|
350
446
|
|
|
351
|
-
return
|
|
447
|
+
return item.separator === true
|
|
448
|
+
? [h("hr", { class: "separator", role: "separator" }), button]
|
|
449
|
+
: [button];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
#tick() {
|
|
453
|
+
const icon = createIcon(tick);
|
|
454
|
+
|
|
455
|
+
icon.classList.add("tick");
|
|
456
|
+
|
|
457
|
+
return icon;
|
|
352
458
|
}
|
|
353
459
|
|
|
354
460
|
protected override create(root: ShadowRoot) {
|
|
@@ -359,14 +465,30 @@ export class GwMenuButton extends GwElement {
|
|
|
359
465
|
);
|
|
360
466
|
this.#popover.addEventListener("toggle", this.#onToggle);
|
|
361
467
|
this.#popover.addEventListener("keydown", this.#onKeydown);
|
|
362
|
-
root.append(this.#trigger, this.#popover);
|
|
468
|
+
root.append(this.#heading, this.#trigger, this.#popover);
|
|
363
469
|
}
|
|
364
470
|
|
|
365
471
|
protected override update(changed: ReadonlySet<string>) {
|
|
366
|
-
this.#triggerText.
|
|
472
|
+
this.#triggerText.textContent = this.label;
|
|
367
473
|
setIconPath(this.#triggerIcon, this.icon);
|
|
368
474
|
this.#triggerIcon.toggleAttribute("hidden", !this.icon);
|
|
369
|
-
this.#
|
|
475
|
+
this.#trigger.classList.toggle("has-icon", this.icon !== "");
|
|
476
|
+
this.#heading.textContent = this.heading;
|
|
477
|
+
setAttribute(
|
|
478
|
+
this.#trigger,
|
|
479
|
+
"aria-labelledby",
|
|
480
|
+
this.heading ? "heading label" : null,
|
|
481
|
+
);
|
|
482
|
+
// A tooltip saying what the menu is for, or the text when it's hidden
|
|
483
|
+
setAttribute(
|
|
484
|
+
this.#trigger,
|
|
485
|
+
"title",
|
|
486
|
+
this.heading ||
|
|
487
|
+
(this.icon !== "" && this.labels !== "show"
|
|
488
|
+
? this.label || null
|
|
489
|
+
: null),
|
|
490
|
+
);
|
|
491
|
+
this.#popover.setAttribute("aria-label", this.heading || this.label);
|
|
370
492
|
|
|
371
493
|
if (changed.has("items") || changed.has("empty")) {
|
|
372
494
|
const items = Array.isArray(this.items) ? this.items : [];
|
|
@@ -374,7 +496,7 @@ export class GwMenuButton extends GwElement {
|
|
|
374
496
|
this.#items.replaceChildren(
|
|
375
497
|
...(items.length === 0
|
|
376
498
|
? [h("p", { class: "empty", role: "none" }, this.empty)]
|
|
377
|
-
: items.
|
|
499
|
+
: items.flatMap((item) => this.#renderItem(item, "item"))),
|
|
378
500
|
);
|
|
379
501
|
}
|
|
380
502
|
|
|
@@ -382,7 +504,7 @@ export class GwMenuButton extends GwElement {
|
|
|
382
504
|
const actions = Array.isArray(this.actions) ? this.actions : [];
|
|
383
505
|
|
|
384
506
|
this.#actions.replaceChildren(
|
|
385
|
-
...actions.
|
|
507
|
+
...actions.flatMap((item) => this.#renderItem(item, "action")),
|
|
386
508
|
);
|
|
387
509
|
this.#actions.hidden = actions.length === 0;
|
|
388
510
|
}
|
package/src/gw-section-title.ts
CHANGED
|
@@ -48,7 +48,11 @@ export class GwSectionTitle extends GwElement {
|
|
|
48
48
|
|
|
49
49
|
declare level: number;
|
|
50
50
|
|
|
51
|
-
readonly #heading
|
|
51
|
+
readonly #heading: HTMLDivElement = h(
|
|
52
|
+
"div",
|
|
53
|
+
{ role: "heading", part: "heading" },
|
|
54
|
+
h("slot"),
|
|
55
|
+
);
|
|
52
56
|
|
|
53
57
|
constructor() {
|
|
54
58
|
super();
|
package/src/gw-setting-row.ts
CHANGED
|
@@ -115,7 +115,7 @@ export class GwSettingRow extends GwElement {
|
|
|
115
115
|
|
|
116
116
|
readonly #description = h("div", { class: "description" });
|
|
117
117
|
|
|
118
|
-
readonly #descriptionSkeleton = h(
|
|
118
|
+
readonly #descriptionSkeleton: HTMLDivElement = h(
|
|
119
119
|
"div",
|
|
120
120
|
{ class: "description" },
|
|
121
121
|
h("gw-skeleton-text", { style: "--gw-skeleton-width: 12rem" }),
|