@timmo001/groundwork-ui 0.9.25 → 0.9.26
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 +104 -11
- package/package.json +1 -1
- package/src/gw-button.ts +19 -1
- package/src/gw-card.ts +39 -4
- package/src/gw-facts.ts +8 -2
- package/src/gw-input.ts +5 -5
- package/src/gw-page-header.ts +37 -2
package/custom-elements.json
CHANGED
|
@@ -3118,7 +3118,7 @@
|
|
|
3118
3118
|
"text": "Properties"
|
|
3119
3119
|
},
|
|
3120
3120
|
"static": true,
|
|
3121
|
-
"default": "{ variant: { reflect: true }, appearance: { reflect: true }, size: { reflect: true }, type: {}, href: {}, label: {}, disabled: { type: \"boolean\", reflect: true }, loading: { type: \"boolean\", reflect: true }, icon: {}, swatch: {}, labels: { reflect: true }, }",
|
|
3121
|
+
"default": "{ variant: { reflect: true }, appearance: { reflect: true }, size: { reflect: true }, type: {}, href: {}, label: {}, disabled: { type: \"boolean\", reflect: true }, loading: { type: \"boolean\", reflect: true }, icon: {}, swatch: {}, labels: { reflect: true }, toggle: { type: \"boolean\", reflect: true }, pressed: { type: \"boolean\", reflect: true }, }",
|
|
3122
3122
|
"inheritedFrom": {
|
|
3123
3123
|
"name": "GwElement",
|
|
3124
3124
|
"module": "src/element.ts"
|
|
@@ -3145,7 +3145,7 @@
|
|
|
3145
3145
|
"text": "readonly CSSStyleSheet[]"
|
|
3146
3146
|
},
|
|
3147
3147
|
"static": true,
|
|
3148
|
-
"default": "[ base, controlSizes(\"button\"), css` :host { --gw-button-tone: var(--gw-color-accent); display: inline-block; } :host([variant=\"neutral\"]) { --gw-button-tone: var(--gw-color-text-secondary); --gw-button-ink: var(--gw-color-text-strong); } :host([variant=\"info\"]) { --gw-button-tone: var(--gw-color-info); } :host([variant=\"success\"]) { --gw-button-tone: var(--gw-color-success); } :host([variant=\"warning\"]) { --gw-button-tone: var(--gw-color-warning); } :host([variant=\"danger\"]) { --gw-button-tone: var(--gw-color-danger); } .button { --gw-size: var(--gw-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.125rem * var(--gw-scale)); position: relative; display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; justify-content: center; min-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); line-height: 1; color: var(--gw-button-ink, var(--gw-button-tone)); text-decoration: none; text-transform: var(--gw-label-transform); letter-spacing: var(--gw-label-tracking); white-space: nowrap; cursor: pointer; background: transparent; border: max(var(--gw-border-width), 1px) solid transparent; border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing), border-color var(--gw-motion-fast) var(--gw-motion-easing), color var(--gw-motion-fast) var(--gw-motion-easing), scale var(--gw-motion-fast) var(--gw-motion-easing); } /* A slight press, so a click feels physical */ .button:active { scale: 0.97; } .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } .button:active { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } /* A solid fill, with text that flips between black and white by the fill's lightness so it reads on every theme's colors */ :host([appearance=\"accent\"]) .button { color: oklch( from var(--gw-button-tone) clamp(0, (0.66 - l) * 1000, 1) 0 0 ); background: var(--gw-button-tone); } :host([appearance=\"accent\"]) .button:hover { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.2) c h ); } :host([appearance=\"accent\"]) .button:active { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.35) c h ); } /* The tint lowers the tone's contrast, so the text leans toward the strongest text color to stay readable on it */ :host([appearance=\"filled\"]) .button { color: var( --gw-button-ink, color-mix( in oklch, var(--gw-button-tone) 70%, var(--gw-color-text-strong) ) ); background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } :host([appearance=\"filled\"]) .button:active { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } :host([appearance=\"outlined\"]) .button { border-color: var(--gw-button-tone); } /* A slotted link carries the text; the pill draws its focus ring */ ::slotted(a) { color: inherit; text-decoration: none; outline: none; } .button.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :host([appearance=\"plain\"]) .button.current, :host([appearance=\"outlined\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } .button:disabled, .button[aria-disabled=\"true\"] { color: var(--gw-color-text-disabled); pointer-events: none; cursor: not-allowed; background: var(--gw-color-fill-neutral-disabled); border-color: transparent; } :host([appearance=\"plain\"]) .button:disabled, :host([appearance=\"plain\"]) .button[aria-disabled=\"true\"] { background: transparent; } ::slotted([slot=\"icon\"]), .icon { flex: none; width: var(--gw-icon-size); height: var(--gw-icon-size); fill: currentColor; } /* The icon and the ring share one cell and crossfade */ .lead { display: grid; place-items: center; } /* The icon slot is display: contents, so its slotted icon is a grid item of its own and needs the shared cell too */ .lead > *, ::slotted([slot=\"icon\"]) { grid-area: 1 / 1; } /* A color dot in the icon's place */ .swatch { width: calc(var(--gw-icon-size) * 0.6); height: calc(var(--gw-icon-size) * 0.6); border: 1px solid color-mix(in oklab, var(--gw-color-text-primary) 20%, transparent); border-radius: 50%; } .lead > *, .text, .ring { transition: opacity var(--gw-motion-normal) cubic-bezier(0.4, 0, 0.2, 1); } /* Sized to the icon's drawn area, which is inset in its 24px box */ .ring { width: calc(var(--gw-icon-size) * 0.8); height: calc(var(--gw-icon-size) * 0.8); border: calc(2px * var(--gw-scale)) solid color-mix(in oklch, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; opacity: 0; animation: spin 0.9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Without an icon, the ring sits over the hidden text */ .ring.over { position: absolute; inset: 0; margin: auto; } :host([loading]) .button { cursor: progress; } /* The icon stays for --gw-motion-delay, so a quick action never flashes the ring, then they crossfade; ending is immediate */ :host([loading]) .lead > .ring, :host([loading]) .button:not(.has-icon) > .ring.over { opacity: 1; transition-delay: var(--gw-motion-delay, 500ms); } :host([loading]) .lead > :not(.ring), :host([loading]) .button:not(.has-icon) > .text { opacity: 0; transition-delay: var(--gw-motion-delay, 500ms); } .button:not(.has-icon) > .lead { display: none; } /* Icon only, to save space: square, with the text kept for assistive technology. auto does this on narrow screens */ :host([labels=\"hide\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"hide\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @media (width < 40rem) { :host([labels=\"auto\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"auto\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } } `, ]",
|
|
3148
|
+
"default": "[ base, controlSizes(\"button\"), css` :host { --gw-button-tone: var(--gw-color-accent); display: inline-block; } :host([variant=\"neutral\"]) { --gw-button-tone: var(--gw-color-text-secondary); --gw-button-ink: var(--gw-color-text-strong); } :host([variant=\"info\"]) { --gw-button-tone: var(--gw-color-info); } :host([variant=\"success\"]) { --gw-button-tone: var(--gw-color-success); } :host([variant=\"warning\"]) { --gw-button-tone: var(--gw-color-warning); } :host([variant=\"danger\"]) { --gw-button-tone: var(--gw-color-danger); } .button { --gw-size: var(--gw-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.125rem * var(--gw-scale)); position: relative; display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; justify-content: center; min-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); line-height: 1; color: var(--gw-button-ink, var(--gw-button-tone)); text-decoration: none; text-transform: var(--gw-label-transform); letter-spacing: var(--gw-label-tracking); white-space: nowrap; cursor: pointer; background: transparent; border: max(var(--gw-border-width), 1px) solid transparent; border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing), border-color var(--gw-motion-fast) var(--gw-motion-easing), color var(--gw-motion-fast) var(--gw-motion-easing), scale var(--gw-motion-fast) var(--gw-motion-easing); } /* A slight press, so a click feels physical */ .button:active { scale: 0.97; } .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } .button:active { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } /* A solid fill, with text that flips between black and white by the fill's lightness so it reads on every theme's colors */ :host([appearance=\"accent\"]) .button { color: oklch( from var(--gw-button-tone) clamp(0, (0.66 - l) * 1000, 1) 0 0 ); background: var(--gw-button-tone); } :host([appearance=\"accent\"]) .button:hover { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.2) c h ); } :host([appearance=\"accent\"]) .button:active { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.35) c h ); } /* The tint lowers the tone's contrast, so the text leans toward the strongest text color to stay readable on it */ :host([appearance=\"filled\"]) .button { color: var( --gw-button-ink, color-mix( in oklch, var(--gw-button-tone) 70%, var(--gw-color-text-strong) ) ); background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } :host([appearance=\"filled\"]) .button:active { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } :host([appearance=\"outlined\"]) .button { border-color: var(--gw-button-tone); } /* A slotted link carries the text; the pill draws its focus ring */ ::slotted(a) { color: inherit; text-decoration: none; outline: none; } .button.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :host([appearance=\"plain\"]) .button.current, :host([appearance=\"outlined\"]) .button.current, :host([toggle][pressed]:is([appearance=\"plain\"], [appearance=\"outlined\"])) .button { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([toggle][pressed][appearance=\"filled\"]) .button, :host([appearance=\"filled\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } .button:disabled, .button[aria-disabled=\"true\"] { color: var(--gw-color-text-disabled); pointer-events: none; cursor: not-allowed; background: var(--gw-color-fill-neutral-disabled); border-color: transparent; } :host([appearance=\"plain\"]) .button:disabled, :host([appearance=\"plain\"]) .button[aria-disabled=\"true\"] { background: transparent; } ::slotted([slot=\"icon\"]), .icon { flex: none; width: var(--gw-icon-size); height: var(--gw-icon-size); fill: currentColor; } /* The icon and the ring share one cell and crossfade */ .lead { display: grid; place-items: center; } /* The icon slot is display: contents, so its slotted icon is a grid item of its own and needs the shared cell too */ .lead > *, ::slotted([slot=\"icon\"]) { grid-area: 1 / 1; } /* A color dot in the icon's place */ .swatch { width: calc(var(--gw-icon-size) * 0.6); height: calc(var(--gw-icon-size) * 0.6); border: 1px solid color-mix(in oklab, var(--gw-color-text-primary) 20%, transparent); border-radius: 50%; } .lead > *, .text, .ring { transition: opacity var(--gw-motion-normal) cubic-bezier(0.4, 0, 0.2, 1); } /* Sized to the icon's drawn area, which is inset in its 24px box */ .ring { width: calc(var(--gw-icon-size) * 0.8); height: calc(var(--gw-icon-size) * 0.8); border: calc(2px * var(--gw-scale)) solid color-mix(in oklch, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; opacity: 0; animation: spin 0.9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Without an icon, the ring sits over the hidden text */ .ring.over { position: absolute; inset: 0; margin: auto; } :host([loading]) .button { cursor: progress; } /* The icon stays for --gw-motion-delay, so a quick action never flashes the ring, then they crossfade; ending is immediate */ :host([loading]) .lead > .ring, :host([loading]) .button:not(.has-icon) > .ring.over { opacity: 1; transition-delay: var(--gw-motion-delay, 500ms); } :host([loading]) .lead > :not(.ring), :host([loading]) .button:not(.has-icon) > .text { opacity: 0; transition-delay: var(--gw-motion-delay, 500ms); } .button:not(.has-icon) > .lead { display: none; } /* Icon only, to save space: square, with the text kept for assistive technology. auto does this on narrow screens */ :host([labels=\"hide\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"hide\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @media (width < 40rem) { :host([labels=\"auto\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"auto\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } } `, ]",
|
|
3149
3149
|
"inheritedFrom": {
|
|
3150
3150
|
"name": "GwElement",
|
|
3151
3151
|
"module": "src/element.ts"
|
|
@@ -3243,6 +3243,24 @@
|
|
|
3243
3243
|
"description": "Whether the text shows beside an icon: always, never, or not on narrow screens.",
|
|
3244
3244
|
"default": "\"show\""
|
|
3245
3245
|
},
|
|
3246
|
+
{
|
|
3247
|
+
"kind": "field",
|
|
3248
|
+
"name": "toggle",
|
|
3249
|
+
"type": {
|
|
3250
|
+
"text": "boolean"
|
|
3251
|
+
},
|
|
3252
|
+
"description": "Makes it a toggle button, announced as pressed or not.",
|
|
3253
|
+
"default": "false"
|
|
3254
|
+
},
|
|
3255
|
+
{
|
|
3256
|
+
"kind": "field",
|
|
3257
|
+
"name": "pressed",
|
|
3258
|
+
"type": {
|
|
3259
|
+
"text": "boolean"
|
|
3260
|
+
},
|
|
3261
|
+
"description": "Whether a toggle button is on.",
|
|
3262
|
+
"default": "false"
|
|
3263
|
+
},
|
|
3246
3264
|
{
|
|
3247
3265
|
"kind": "field",
|
|
3248
3266
|
"name": "#icon",
|
|
@@ -3679,6 +3697,20 @@
|
|
|
3679
3697
|
},
|
|
3680
3698
|
"description": "Whether the text shows beside an icon: always, never, or not on narrow screens. Defaults to `show`.",
|
|
3681
3699
|
"name": "labels"
|
|
3700
|
+
},
|
|
3701
|
+
{
|
|
3702
|
+
"type": {
|
|
3703
|
+
"text": "boolean"
|
|
3704
|
+
},
|
|
3705
|
+
"description": "Makes it a toggle button, which assistive technology announces as pressed or not.",
|
|
3706
|
+
"name": "toggle"
|
|
3707
|
+
},
|
|
3708
|
+
{
|
|
3709
|
+
"type": {
|
|
3710
|
+
"text": "boolean"
|
|
3711
|
+
},
|
|
3712
|
+
"description": "Whether a toggle button is on. It shows the same tint as a current link.",
|
|
3713
|
+
"name": "pressed"
|
|
3682
3714
|
}
|
|
3683
3715
|
],
|
|
3684
3716
|
"superclass": {
|
|
@@ -3761,7 +3793,7 @@
|
|
|
3761
3793
|
"text": "Properties"
|
|
3762
3794
|
},
|
|
3763
3795
|
"static": true,
|
|
3764
|
-
"default": "{ heading: {}, description: {}, level: { type: \"number\" }, href: {}, flush: { type: \"boolean\", reflect: true }, loading: { type: \"boolean\", reflect: true }, lines: { type: \"number\" }, }",
|
|
3796
|
+
"default": "{ heading: {}, description: {}, icon: {}, level: { type: \"number\" }, href: {}, flush: { type: \"boolean\", reflect: true }, loading: { type: \"boolean\", reflect: true }, lines: { type: \"number\" }, }",
|
|
3765
3797
|
"inheritedFrom": {
|
|
3766
3798
|
"name": "GwElement",
|
|
3767
3799
|
"module": "src/element.ts"
|
|
@@ -3774,7 +3806,7 @@
|
|
|
3774
3806
|
"text": "readonly CSSStyleSheet[]"
|
|
3775
3807
|
},
|
|
3776
3808
|
"static": true,
|
|
3777
|
-
"default": "[ base, css` :host { display: block; } .card { display: flex; flex-direction: column; gap: var(--gw-space-3); height: 100%; padding: var(--gw-space-4); color: inherit; text-decoration: none; background: var(--gw-color-surface-raised); border: var(--gw-border-width) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-panel); box-shadow: var(--gw-shadow-s); } :host([flush]) .card { padding: 0; overflow: hidden; } a.card { transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } a.card:hover, .card.linked:hover { border-color: var(--gw-color-border-accent); } /* A card following a link in its heading slot */ .card.linked { cursor: pointer; transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } .card.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } ::slotted([slot=\"heading\"]) { font: inherit; color: inherit; } ::slotted(a[slot=\"heading\"]) { text-decoration: none; outline: none; } header { display: flex; gap: var(--gw-space-2); align-items: center; } .titles { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; } .title { margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .description { font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } /* The divider runs to the card's edges */ footer:not([hidden]) { display: flex; flex-direction: column; gap: inherit; margin-inline: calc(-1 * var(--gw-space-4)); padding: var(--gw-space-3) var(--gw-space-4) 0; border-top: var(--gw-border-width) solid var(--gw-color-border-normal); } :host([flush]) footer:not([hidden]) { margin-inline: 0; padding-bottom: var(--gw-space-3); } .body:not([hidden]) { display: flex; flex-direction: column; gap: inherit; } `, ]",
|
|
3809
|
+
"default": "[ base, css` :host { display: block; } .card { display: flex; flex-direction: column; gap: var(--gw-space-3); height: 100%; padding: var(--gw-space-4); color: inherit; text-decoration: none; background: var(--gw-color-surface-raised); border: var(--gw-border-width) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-panel); box-shadow: var(--gw-shadow-s); } :host([flush]) .card { padding: 0; overflow: hidden; } a.card { transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } a.card:hover, .card.linked:hover { border-color: var(--gw-color-border-accent); } /* A card following a link in its heading slot */ .card.linked { cursor: pointer; transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } .card.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } ::slotted([slot=\"heading\"]) { font: inherit; color: inherit; } ::slotted(a[slot=\"heading\"]) { text-decoration: none; outline: none; } header { display: flex; gap: var(--gw-space-2); align-items: center; } header > .icon { flex-shrink: 0; margin-inline-end: var(--gw-space-1); color: var(--gw-color-accent); } .titles { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; } .title { margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .description { font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } /* The divider runs to the card's edges */ footer:not([hidden]) { display: flex; flex-direction: column; gap: inherit; margin-inline: calc(-1 * var(--gw-space-4)); padding: var(--gw-space-3) var(--gw-space-4) 0; border-top: var(--gw-border-width) solid var(--gw-color-border-normal); } :host([flush]) footer:not([hidden]) { margin-inline: 0; padding-bottom: var(--gw-space-3); } .body:not([hidden]) { display: flex; flex-direction: column; gap: inherit; } `, ]",
|
|
3778
3810
|
"inheritedFrom": {
|
|
3779
3811
|
"name": "GwElement",
|
|
3780
3812
|
"module": "src/element.ts"
|
|
@@ -3796,6 +3828,15 @@
|
|
|
3796
3828
|
},
|
|
3797
3829
|
"default": "\"\""
|
|
3798
3830
|
},
|
|
3831
|
+
{
|
|
3832
|
+
"kind": "field",
|
|
3833
|
+
"name": "icon",
|
|
3834
|
+
"type": {
|
|
3835
|
+
"text": "string"
|
|
3836
|
+
},
|
|
3837
|
+
"description": "SVG path data for an icon before the heading.",
|
|
3838
|
+
"default": "\"\""
|
|
3839
|
+
},
|
|
3799
3840
|
{
|
|
3800
3841
|
"kind": "field",
|
|
3801
3842
|
"name": "level",
|
|
@@ -3868,6 +3909,12 @@
|
|
|
3868
3909
|
"privacy": "private",
|
|
3869
3910
|
"readonly": true
|
|
3870
3911
|
},
|
|
3912
|
+
{
|
|
3913
|
+
"kind": "field",
|
|
3914
|
+
"name": "#icon",
|
|
3915
|
+
"privacy": "private",
|
|
3916
|
+
"readonly": true
|
|
3917
|
+
},
|
|
3871
3918
|
{
|
|
3872
3919
|
"kind": "field",
|
|
3873
3920
|
"name": "#header",
|
|
@@ -3898,6 +3945,21 @@
|
|
|
3898
3945
|
"privacy": "private",
|
|
3899
3946
|
"readonly": true
|
|
3900
3947
|
},
|
|
3948
|
+
{
|
|
3949
|
+
"kind": "field",
|
|
3950
|
+
"name": "#bodySlot",
|
|
3951
|
+
"privacy": "private",
|
|
3952
|
+
"readonly": true
|
|
3953
|
+
},
|
|
3954
|
+
{
|
|
3955
|
+
"kind": "field",
|
|
3956
|
+
"name": "#empty",
|
|
3957
|
+
"privacy": "private",
|
|
3958
|
+
"type": {
|
|
3959
|
+
"text": "boolean"
|
|
3960
|
+
},
|
|
3961
|
+
"default": "true"
|
|
3962
|
+
},
|
|
3901
3963
|
{
|
|
3902
3964
|
"kind": "field",
|
|
3903
3965
|
"name": "#body",
|
|
@@ -4271,6 +4333,13 @@
|
|
|
4271
4333
|
"description": "A line under the heading.",
|
|
4272
4334
|
"name": "description"
|
|
4273
4335
|
},
|
|
4336
|
+
{
|
|
4337
|
+
"type": {
|
|
4338
|
+
"text": "string"
|
|
4339
|
+
},
|
|
4340
|
+
"description": "SVG path data for an icon before the heading, such as on a card that links to a section.",
|
|
4341
|
+
"name": "icon"
|
|
4342
|
+
},
|
|
4274
4343
|
{
|
|
4275
4344
|
"type": {
|
|
4276
4345
|
"text": "number"
|
|
@@ -8319,6 +8388,12 @@
|
|
|
8319
8388
|
"name": "value"
|
|
8320
8389
|
}
|
|
8321
8390
|
],
|
|
8391
|
+
"slots": [
|
|
8392
|
+
{
|
|
8393
|
+
"description": "`<dt>` and `<dd>` pairs, in place of `items`, for values with markup such as chips or code.",
|
|
8394
|
+
"name": ""
|
|
8395
|
+
}
|
|
8396
|
+
],
|
|
8322
8397
|
"members": [
|
|
8323
8398
|
{
|
|
8324
8399
|
"kind": "field",
|
|
@@ -8340,7 +8415,7 @@
|
|
|
8340
8415
|
"text": "readonly CSSStyleSheet[]"
|
|
8341
8416
|
},
|
|
8342
8417
|
"static": true,
|
|
8343
|
-
"default": "[ base, css` :host { display: block; } dl { display: grid; grid-template-columns: minmax(7rem, max-content) 1fr; gap: var(--gw-space-1) var(--gw-space-3); margin: 0; font-size: var(--gw-font-size-s); } dt { color: var(--gw-color-text-secondary); } dd { min-width: 0; margin: 0; font-variant-numeric: var(--gw-font-figures); color: var(--gw-color-text-strong); overflow-wrap: anywhere; } `, ]",
|
|
8418
|
+
"default": "[ base, css` :host { display: block; } dl { display: grid; grid-template-columns: minmax(7rem, max-content) 1fr; gap: var(--gw-space-1) var(--gw-space-3); margin: 0; font-size: var(--gw-font-size-s); } dt, ::slotted(dt) { color: var(--gw-color-text-secondary); } dd, ::slotted(dd) { min-width: 0; margin: 0; font-variant-numeric: var(--gw-font-figures); color: var(--gw-color-text-strong); overflow-wrap: anywhere; } `, ]",
|
|
8344
8419
|
"inheritedFrom": {
|
|
8345
8420
|
"name": "GwElement",
|
|
8346
8421
|
"module": "src/element.ts"
|
|
@@ -9857,7 +9932,7 @@
|
|
|
9857
9932
|
"text": "Properties"
|
|
9858
9933
|
},
|
|
9859
9934
|
"static": true,
|
|
9860
|
-
"default": "{ label: {}, name: {}, type: {}, value: {}, placeholder: {}, autocomplete: {}, inputmode: {}, min: {}, max: {}, step: {}, hint: {}, info: {}, error: {}, required: { type: \"boolean\" }, optional: { type: \"boolean\" }, disabled: { type: \"boolean\" }, multiline: { type: \"boolean\", reflect: true }, rows: { type: \"number\" }, monospace: { type: \"boolean\", reflect: true }, reveal: { type: \"boolean\" }, size: { reflect: true }, icon: {},
|
|
9935
|
+
"default": "{ label: {}, name: {}, type: {}, value: {}, placeholder: {}, autocomplete: {}, inputmode: {}, min: {}, max: {}, step: {}, hint: {}, info: {}, error: {}, required: { type: \"boolean\" }, optional: { type: \"boolean\" }, disabled: { type: \"boolean\" }, multiline: { type: \"boolean\", reflect: true }, rows: { type: \"number\" }, monospace: { type: \"boolean\", reflect: true }, reveal: { type: \"boolean\" }, size: { reflect: true }, icon: {}, labelHidden: { type: \"boolean\", attribute: \"label-hidden\", reflect: true }, }",
|
|
9861
9936
|
"inheritedFrom": {
|
|
9862
9937
|
"name": "GwElement",
|
|
9863
9938
|
"module": "src/element.ts"
|
|
@@ -10052,7 +10127,7 @@
|
|
|
10052
10127
|
},
|
|
10053
10128
|
{
|
|
10054
10129
|
"kind": "field",
|
|
10055
|
-
"name": "
|
|
10130
|
+
"name": "labelHidden",
|
|
10056
10131
|
"type": {
|
|
10057
10132
|
"text": "boolean"
|
|
10058
10133
|
},
|
|
@@ -10627,7 +10702,7 @@
|
|
|
10627
10702
|
"text": "boolean"
|
|
10628
10703
|
},
|
|
10629
10704
|
"description": "Keeps the label as the field's accessible name but doesn't show it, for a field in a toolbar whose icon and placeholder say what it's for.",
|
|
10630
|
-
"name": "
|
|
10705
|
+
"name": "label-hidden"
|
|
10631
10706
|
}
|
|
10632
10707
|
],
|
|
10633
10708
|
"superclass": {
|
|
@@ -13769,7 +13844,7 @@
|
|
|
13769
13844
|
"text": "Properties"
|
|
13770
13845
|
},
|
|
13771
13846
|
"static": true,
|
|
13772
|
-
"default": "{ heading: {}, description: {}, icon: {}, }",
|
|
13847
|
+
"default": "{ heading: {}, description: {}, icon: {}, level: { type: \"number\", reflect: true }, }",
|
|
13773
13848
|
"inheritedFrom": {
|
|
13774
13849
|
"name": "GwElement",
|
|
13775
13850
|
"module": "src/element.ts"
|
|
@@ -13782,7 +13857,7 @@
|
|
|
13782
13857
|
"text": "readonly CSSStyleSheet[]"
|
|
13783
13858
|
},
|
|
13784
13859
|
"static": true,
|
|
13785
|
-
"default": "[ base, css` :host { display: flex; gap: var(--gw-space-3); align-items: flex-start; } .start { display: flex; flex: none; align-items: center; min-height: 3rem; } .start[hidden] { display: none; } .tile { display: grid; flex: none; place-items: center; width: 3rem; height: 3rem; color: var(--gw-color-accent); background: var(--gw-color-fill-accent-quiet); border-radius: var(--gw-radius-l); --gw-icon-size: 1.625rem; } .titles { display: flex; flex: 0 1 auto; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; }
|
|
13860
|
+
"default": "[ base, css` :host { display: flex; gap: var(--gw-space-3); align-items: flex-start; } .start { display: flex; flex: none; align-items: center; min-height: 3rem; } .start[hidden] { display: none; } .tile { display: grid; flex: none; place-items: center; width: 3rem; height: 3rem; color: var(--gw-color-accent); background: var(--gw-color-fill-accent-quiet); border-radius: var(--gw-radius-l); --gw-icon-size: 1.625rem; } .titles { display: flex; flex: 0 1 auto; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; } :host(:not([level=\"1\"])) .tile { width: 2.5rem; height: 2.5rem; border-radius: var(--gw-radius-m); --gw-icon-size: 1.375rem; } :host(:not([level=\"1\"])) .heading { font-size: var(--gw-font-size-l); } :host(:not([level=\"1\"])) .tile:not([hidden]) ~ .actions { min-height: 2.5rem; } .heading { margin: 0; font-size: var(--gw-font-size-2xl); font-weight: var(--gw-font-weight-strong); line-height: 1.2; color: var(--gw-color-text-strong); } p { margin: 0; font-size: var(--gw-font-size-s); color: var(--gw-color-text-secondary); } /* Takes the room the titles leave, and gives it up before them, down to its items' smallest */ .actions { display: flex; flex: 1 1000 auto; gap: var(--gw-space-1); align-items: center; justify-content: flex-end; } /* Centered on the tile when there is one */ .tile:not([hidden]) ~ .actions { min-height: 3rem; } `, ]",
|
|
13786
13861
|
"inheritedFrom": {
|
|
13787
13862
|
"name": "GwElement",
|
|
13788
13863
|
"module": "src/element.ts"
|
|
@@ -13812,6 +13887,15 @@
|
|
|
13812
13887
|
},
|
|
13813
13888
|
"default": "\"\""
|
|
13814
13889
|
},
|
|
13890
|
+
{
|
|
13891
|
+
"kind": "field",
|
|
13892
|
+
"name": "level",
|
|
13893
|
+
"type": {
|
|
13894
|
+
"text": "number"
|
|
13895
|
+
},
|
|
13896
|
+
"description": "The heading level, 1 to 6.",
|
|
13897
|
+
"default": "1"
|
|
13898
|
+
},
|
|
13815
13899
|
{
|
|
13816
13900
|
"kind": "field",
|
|
13817
13901
|
"name": "#icon",
|
|
@@ -13840,7 +13924,9 @@
|
|
|
13840
13924
|
"kind": "field",
|
|
13841
13925
|
"name": "#heading",
|
|
13842
13926
|
"privacy": "private",
|
|
13843
|
-
"
|
|
13927
|
+
"type": {
|
|
13928
|
+
"text": "HTMLHeadingElement"
|
|
13929
|
+
}
|
|
13844
13930
|
},
|
|
13845
13931
|
{
|
|
13846
13932
|
"kind": "field",
|
|
@@ -14181,6 +14267,13 @@
|
|
|
14181
14267
|
},
|
|
14182
14268
|
"description": "SVG path data for the icon tile.",
|
|
14183
14269
|
"name": "icon"
|
|
14270
|
+
},
|
|
14271
|
+
{
|
|
14272
|
+
"type": {
|
|
14273
|
+
"text": "number"
|
|
14274
|
+
},
|
|
14275
|
+
"description": "The heading level, 1 to 6. Defaults to 1. From 2 the header is smaller, for a section of a page that reads like a page of its own.",
|
|
14276
|
+
"name": "level"
|
|
14184
14277
|
}
|
|
14185
14278
|
],
|
|
14186
14279
|
"superclass": {
|
package/package.json
CHANGED
package/src/gw-button.ts
CHANGED
|
@@ -40,6 +40,8 @@ import { base, controlSizes, createIcon, setIconPath } from "./styles.js";
|
|
|
40
40
|
* @attr {string} icon - SVG path data for an icon before the text.
|
|
41
41
|
* @attr {string} swatch - A CSS color, drawn as a small round dot in the icon's place, such as a tag's own color.
|
|
42
42
|
* @attr {"show"|"hide"|"auto"} labels - Whether the text shows beside an icon: always, never, or not on narrow screens. Defaults to `show`.
|
|
43
|
+
* @attr {boolean} toggle - Makes it a toggle button, which assistive technology announces as pressed or not.
|
|
44
|
+
* @attr {boolean} pressed - Whether a toggle button is on. It shows the same tint as a current link.
|
|
43
45
|
*
|
|
44
46
|
* @slot - The text, or an `<a>` holding it, in place of `href`.
|
|
45
47
|
* @slot icon - An icon before the text, in place of `icon`.
|
|
@@ -64,6 +66,8 @@ export class GwButton extends GwElement {
|
|
|
64
66
|
icon: {},
|
|
65
67
|
swatch: {},
|
|
66
68
|
labels: { reflect: true },
|
|
69
|
+
toggle: { type: "boolean", reflect: true },
|
|
70
|
+
pressed: { type: "boolean", reflect: true },
|
|
67
71
|
} as const;
|
|
68
72
|
|
|
69
73
|
/** Focusing the element focuses its native button, such as from a dialog opening. */
|
|
@@ -208,10 +212,13 @@ export class GwButton extends GwElement {
|
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
:host([appearance="plain"]) .button.current,
|
|
211
|
-
:host([appearance="outlined"]) .button.current
|
|
215
|
+
:host([appearance="outlined"]) .button.current,
|
|
216
|
+
:host([toggle][pressed]:is([appearance="plain"], [appearance="outlined"]))
|
|
217
|
+
.button {
|
|
212
218
|
background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent);
|
|
213
219
|
}
|
|
214
220
|
|
|
221
|
+
:host([toggle][pressed][appearance="filled"]) .button,
|
|
215
222
|
:host([appearance="filled"]) .button.current {
|
|
216
223
|
background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent);
|
|
217
224
|
}
|
|
@@ -363,6 +370,10 @@ export class GwButton extends GwElement {
|
|
|
363
370
|
declare swatch: string;
|
|
364
371
|
/** Whether the text shows beside an icon: always, never, or not on narrow screens. */
|
|
365
372
|
declare labels: "show" | "hide" | "auto";
|
|
373
|
+
/** Makes it a toggle button, announced as pressed or not. */
|
|
374
|
+
declare toggle: boolean;
|
|
375
|
+
/** Whether a toggle button is on. */
|
|
376
|
+
declare pressed: boolean;
|
|
366
377
|
|
|
367
378
|
readonly #icon = createIcon("");
|
|
368
379
|
|
|
@@ -403,6 +414,8 @@ export class GwButton extends GwElement {
|
|
|
403
414
|
this.icon = "";
|
|
404
415
|
this.swatch = "";
|
|
405
416
|
this.labels = "show";
|
|
417
|
+
this.toggle = false;
|
|
418
|
+
this.pressed = false;
|
|
406
419
|
this.variant = "brand";
|
|
407
420
|
this.appearance = "accent";
|
|
408
421
|
this.size = "medium";
|
|
@@ -537,6 +550,11 @@ export class GwButton extends GwElement {
|
|
|
537
550
|
} else if (control instanceof HTMLButtonElement) {
|
|
538
551
|
control.type = this.type;
|
|
539
552
|
control.disabled = this.disabled;
|
|
553
|
+
setAttribute(
|
|
554
|
+
control,
|
|
555
|
+
"aria-pressed",
|
|
556
|
+
this.toggle ? String(this.pressed) : null,
|
|
557
|
+
);
|
|
540
558
|
}
|
|
541
559
|
}
|
|
542
560
|
}
|
package/src/gw-card.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { GwElement, css, h, setAttribute } from "./element.js";
|
|
8
8
|
import "./gw-skeleton.js";
|
|
9
9
|
import { animateHeight, fadeIn } from "./motion.js";
|
|
10
|
-
import { base } from "./styles.js";
|
|
10
|
+
import { base, createIcon, setIconPath } from "./styles.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* A surface for one group of content, with an optional heading and actions.
|
|
@@ -26,6 +26,7 @@ import { base } from "./styles.js";
|
|
|
26
26
|
*
|
|
27
27
|
* @attr {string} heading - The card's heading.
|
|
28
28
|
* @attr {string} description - A line under the heading.
|
|
29
|
+
* @attr {string} icon - SVG path data for an icon before the heading, such as on a card that links to a section.
|
|
29
30
|
* @attr {number} level - The heading's level for assistive tech, 2 by default. Use 3 for a card under a section heading.
|
|
30
31
|
* @attr {string} href - Makes the whole card a link to this address.
|
|
31
32
|
* @attr {boolean} flush - Drops the padding, for content that runs to the edges.
|
|
@@ -48,6 +49,7 @@ export class GwCard extends GwElement {
|
|
|
48
49
|
static override properties = {
|
|
49
50
|
heading: {},
|
|
50
51
|
description: {},
|
|
52
|
+
icon: {},
|
|
51
53
|
level: { type: "number" },
|
|
52
54
|
href: {},
|
|
53
55
|
flush: { type: "boolean", reflect: true },
|
|
@@ -117,6 +119,12 @@ export class GwCard extends GwElement {
|
|
|
117
119
|
align-items: center;
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
header > .icon {
|
|
123
|
+
flex-shrink: 0;
|
|
124
|
+
margin-inline-end: var(--gw-space-1);
|
|
125
|
+
color: var(--gw-color-accent);
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
.titles {
|
|
121
129
|
display: flex;
|
|
122
130
|
flex: 1;
|
|
@@ -162,6 +170,8 @@ export class GwCard extends GwElement {
|
|
|
162
170
|
|
|
163
171
|
declare heading: string;
|
|
164
172
|
declare description: string;
|
|
173
|
+
/** SVG path data for an icon before the heading. */
|
|
174
|
+
declare icon: string;
|
|
165
175
|
/** The heading's level for assistive tech. */
|
|
166
176
|
declare level: number;
|
|
167
177
|
declare href: string;
|
|
@@ -189,9 +199,12 @@ export class GwCard extends GwElement {
|
|
|
189
199
|
part: "description",
|
|
190
200
|
});
|
|
191
201
|
|
|
202
|
+
readonly #icon = createIcon("");
|
|
203
|
+
|
|
192
204
|
readonly #header: HTMLElement = h(
|
|
193
205
|
"header",
|
|
194
206
|
{ part: "header" },
|
|
207
|
+
this.#icon,
|
|
195
208
|
h("div", { class: "titles" }, this.#heading, this.#description),
|
|
196
209
|
h("slot", { name: "actions" }),
|
|
197
210
|
);
|
|
@@ -206,10 +219,14 @@ export class GwCard extends GwElement {
|
|
|
206
219
|
|
|
207
220
|
readonly #skeleton = h("gw-skeleton-lines");
|
|
208
221
|
|
|
222
|
+
readonly #bodySlot = h("slot");
|
|
223
|
+
|
|
224
|
+
#empty = true;
|
|
225
|
+
|
|
209
226
|
readonly #body: HTMLDivElement = h(
|
|
210
227
|
"div",
|
|
211
228
|
{ class: "body", part: "body" },
|
|
212
|
-
|
|
229
|
+
this.#bodySlot,
|
|
213
230
|
);
|
|
214
231
|
|
|
215
232
|
#card: HTMLElement = h("section", { class: "card", part: "card" });
|
|
@@ -222,6 +239,7 @@ export class GwCard extends GwElement {
|
|
|
222
239
|
super();
|
|
223
240
|
this.heading = "";
|
|
224
241
|
this.description = "";
|
|
242
|
+
this.icon = "";
|
|
225
243
|
this.level = 2;
|
|
226
244
|
this.href = "";
|
|
227
245
|
this.flush = false;
|
|
@@ -274,6 +292,17 @@ export class GwCard extends GwElement {
|
|
|
274
292
|
this.#footer.hidden =
|
|
275
293
|
this.#footerSlot.assignedNodes({ flatten: true }).length === 0;
|
|
276
294
|
});
|
|
295
|
+
|
|
296
|
+
// A card with only a header, such as a link to a section, has no body gap
|
|
297
|
+
this.#bodySlot.addEventListener("slotchange", () => {
|
|
298
|
+
this.#empty = this.#bodySlot
|
|
299
|
+
.assignedNodes({ flatten: true })
|
|
300
|
+
.every(
|
|
301
|
+
(node) =>
|
|
302
|
+
node.nodeType === Node.TEXT_NODE && !node.textContent?.trim(),
|
|
303
|
+
);
|
|
304
|
+
this.requestUpdate("loading");
|
|
305
|
+
});
|
|
277
306
|
}
|
|
278
307
|
|
|
279
308
|
/** The `<a>` in the heading slot, or holding it, when there is one. */
|
|
@@ -322,14 +351,20 @@ export class GwCard extends GwElement {
|
|
|
322
351
|
this.#heading.setAttribute("aria-level", String(this.level || 2));
|
|
323
352
|
this.#description.textContent = this.description;
|
|
324
353
|
this.#description.hidden = !this.description;
|
|
354
|
+
setIconPath(this.#icon, this.icon);
|
|
355
|
+
this.#icon.toggleAttribute("hidden", !this.icon);
|
|
325
356
|
this.#titleSkeleton.hidden =
|
|
326
357
|
this.heading !== "" || slotted || !this.loading;
|
|
327
358
|
this.#header.hidden =
|
|
328
|
-
!this.heading &&
|
|
359
|
+
!this.heading &&
|
|
360
|
+
!this.description &&
|
|
361
|
+
!this.icon &&
|
|
362
|
+
!slotted &&
|
|
363
|
+
!this.loading;
|
|
329
364
|
this.#heading.hidden = !this.heading && !slotted && !this.loading;
|
|
330
365
|
this.#skeleton.lines = this.lines;
|
|
331
366
|
this.#skeleton.hidden = !this.loading;
|
|
332
|
-
this.#body.hidden = this.loading;
|
|
367
|
+
this.#body.hidden = this.loading || this.#empty;
|
|
333
368
|
|
|
334
369
|
if (animate) {
|
|
335
370
|
animateHeight(this, before);
|
package/src/gw-facts.ts
CHANGED
|
@@ -29,6 +29,8 @@ const skeletonWidths = ["60%", "40%", "75%", "50%", "65%"];
|
|
|
29
29
|
* @attr {boolean} loading - Shows skeleton pairs, keeping any labels already known.
|
|
30
30
|
* @attr {number} rows - How many skeleton pairs while loading. Defaults to 4.
|
|
31
31
|
*
|
|
32
|
+
* @slot - `<dt>` and `<dd>` pairs, in place of `items`, for values with markup such as chips or code.
|
|
33
|
+
*
|
|
32
34
|
* @csspart list - The description list.
|
|
33
35
|
* @csspart label - Each label.
|
|
34
36
|
* @csspart value - Each value.
|
|
@@ -55,11 +57,13 @@ export class GwFacts extends GwElement {
|
|
|
55
57
|
font-size: var(--gw-font-size-s);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
dt
|
|
60
|
+
dt,
|
|
61
|
+
::slotted(dt) {
|
|
59
62
|
color: var(--gw-color-text-secondary);
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
dd
|
|
65
|
+
dd,
|
|
66
|
+
::slotted(dd) {
|
|
63
67
|
min-width: 0;
|
|
64
68
|
margin: 0;
|
|
65
69
|
font-variant-numeric: var(--gw-font-figures);
|
|
@@ -117,6 +121,8 @@ export class GwFacts extends GwElement {
|
|
|
117
121
|
} else {
|
|
118
122
|
this.#list.removeAttribute("aria-busy");
|
|
119
123
|
this.#list.replaceChildren(
|
|
124
|
+
// The slot is display: contents, so slotted pairs join the grid
|
|
125
|
+
h("slot"),
|
|
120
126
|
...items.flatMap((item) => [
|
|
121
127
|
h("dt", { part: "label" }, item.label),
|
|
122
128
|
h(
|
package/src/gw-input.ts
CHANGED
|
@@ -46,7 +46,7 @@ const hideIcon =
|
|
|
46
46
|
* @attr {boolean} reveal - On a password field, adds a button at the end to show and hide what's typed.
|
|
47
47
|
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The field's size, 40px tall at medium. Defaults to `medium`.
|
|
48
48
|
* @attr {string} icon - SVG path data for an icon inside the field, before the text, such as a magnifier on a search.
|
|
49
|
-
* @attr {boolean}
|
|
49
|
+
* @attr {boolean} label-hidden - Keeps the label as the field's accessible name but doesn't show it, for a field in a toolbar whose icon and placeholder say what it's for.
|
|
50
50
|
*
|
|
51
51
|
* @cssprop --gw-field-scale - Scales the field, around 1 for medium.
|
|
52
52
|
* @cssprop --gw-control-scale - Scales its height with the theme's density. The theme sets it.
|
|
@@ -75,7 +75,7 @@ export class GwInput extends LightElement {
|
|
|
75
75
|
reveal: { type: "boolean" },
|
|
76
76
|
size: { reflect: true },
|
|
77
77
|
icon: {},
|
|
78
|
-
|
|
78
|
+
labelHidden: { type: "boolean", attribute: "label-hidden", reflect: true },
|
|
79
79
|
} as const;
|
|
80
80
|
|
|
81
81
|
declare label: string;
|
|
@@ -112,7 +112,7 @@ export class GwInput extends LightElement {
|
|
|
112
112
|
/** SVG path data for an icon inside the field, before the text. */
|
|
113
113
|
declare icon: string;
|
|
114
114
|
/** Keeps the label as the accessible name without showing it. */
|
|
115
|
-
declare
|
|
115
|
+
declare labelHidden: boolean;
|
|
116
116
|
|
|
117
117
|
readonly #id = fieldId();
|
|
118
118
|
|
|
@@ -184,7 +184,7 @@ export class GwInput extends LightElement {
|
|
|
184
184
|
this.reveal = false;
|
|
185
185
|
this.size = "medium";
|
|
186
186
|
this.icon = "";
|
|
187
|
-
this.
|
|
187
|
+
this.labelHidden = false;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
protected override create(root: HTMLElement) {
|
|
@@ -259,7 +259,7 @@ export class GwInput extends LightElement {
|
|
|
259
259
|
this.#label.replaceChildren(this.label, this.#optional);
|
|
260
260
|
this.#optional.hidden = !this.optional;
|
|
261
261
|
this.#info.update(this.label, this.info);
|
|
262
|
-
this.#info.heading.classList.toggle("gw-visually-hidden", this.
|
|
262
|
+
this.#info.heading.classList.toggle("gw-visually-hidden", this.labelHidden);
|
|
263
263
|
setIconPath(this.#icon, this.icon);
|
|
264
264
|
this.#icon.toggleAttribute("hidden", !this.icon);
|
|
265
265
|
this.#input.type = reveal && this.#revealed ? "text" : this.type;
|
package/src/gw-page-header.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import { GwElement, css, h } from "./element.js";
|
|
8
8
|
import { base, createIcon, setIconPath } from "./styles.js";
|
|
9
9
|
|
|
10
|
+
const headingTags = ["h1", "h2", "h3", "h4", "h5", "h6"] as const;
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* A page's title, with an optional icon tile and description, a back button
|
|
12
14
|
* or similar in the `start` slot, and its controls at the top right in the
|
|
@@ -18,6 +20,7 @@ import { base, createIcon, setIconPath } from "./styles.js";
|
|
|
18
20
|
* @attr {string} heading - The page's title.
|
|
19
21
|
* @attr {string} description - A line under the title.
|
|
20
22
|
* @attr {string} icon - SVG path data for the icon tile.
|
|
23
|
+
* @attr {number} level - The heading level, 1 to 6. Defaults to 1. From 2 the header is smaller, for a section of a page that reads like a page of its own.
|
|
21
24
|
*
|
|
22
25
|
* @slot start - Before the icon tile, such as a back button.
|
|
23
26
|
* @slot actions - The page's controls.
|
|
@@ -31,6 +34,7 @@ export class GwPageHeader extends GwElement {
|
|
|
31
34
|
heading: {},
|
|
32
35
|
description: {},
|
|
33
36
|
icon: {},
|
|
37
|
+
level: { type: "number", reflect: true },
|
|
34
38
|
} as const;
|
|
35
39
|
|
|
36
40
|
static override styles = [
|
|
@@ -73,7 +77,22 @@ export class GwPageHeader extends GwElement {
|
|
|
73
77
|
min-width: 0;
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
:host(:not([level="1"])) .tile {
|
|
81
|
+
width: 2.5rem;
|
|
82
|
+
height: 2.5rem;
|
|
83
|
+
border-radius: var(--gw-radius-m);
|
|
84
|
+
--gw-icon-size: 1.375rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
:host(:not([level="1"])) .heading {
|
|
88
|
+
font-size: var(--gw-font-size-l);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:host(:not([level="1"])) .tile:not([hidden]) ~ .actions {
|
|
92
|
+
min-height: 2.5rem;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.heading {
|
|
77
96
|
margin: 0;
|
|
78
97
|
font-size: var(--gw-font-size-2xl);
|
|
79
98
|
font-weight: var(--gw-font-weight-strong);
|
|
@@ -107,6 +126,8 @@ export class GwPageHeader extends GwElement {
|
|
|
107
126
|
declare heading: string;
|
|
108
127
|
declare description: string;
|
|
109
128
|
declare icon: string;
|
|
129
|
+
/** The heading level, 1 to 6. */
|
|
130
|
+
declare level: number;
|
|
110
131
|
|
|
111
132
|
readonly #icon = createIcon("");
|
|
112
133
|
|
|
@@ -117,7 +138,7 @@ export class GwPageHeader extends GwElement {
|
|
|
117
138
|
|
|
118
139
|
readonly #tile = h("span", { class: "tile", part: "tile" }, this.#icon);
|
|
119
140
|
|
|
120
|
-
|
|
141
|
+
#heading: HTMLHeadingElement = h("h1", { class: "heading", part: "heading" });
|
|
121
142
|
|
|
122
143
|
readonly #description = h("p", { part: "description" });
|
|
123
144
|
|
|
@@ -126,6 +147,7 @@ export class GwPageHeader extends GwElement {
|
|
|
126
147
|
this.heading = "";
|
|
127
148
|
this.description = "";
|
|
128
149
|
this.icon = "";
|
|
150
|
+
this.level = 1;
|
|
129
151
|
}
|
|
130
152
|
|
|
131
153
|
protected override create(root: ShadowRoot) {
|
|
@@ -141,6 +163,19 @@ export class GwPageHeader extends GwElement {
|
|
|
141
163
|
}
|
|
142
164
|
|
|
143
165
|
protected override update() {
|
|
166
|
+
const level = Math.min(6, Math.max(1, Math.round(this.level) || 1));
|
|
167
|
+
const tag = headingTags[level - 1] ?? "h1";
|
|
168
|
+
|
|
169
|
+
if (this.#heading.localName !== tag) {
|
|
170
|
+
const next = h(tag, {
|
|
171
|
+
class: "heading",
|
|
172
|
+
part: "heading",
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.#heading.replaceWith(next);
|
|
176
|
+
this.#heading = next;
|
|
177
|
+
}
|
|
178
|
+
|
|
144
179
|
setIconPath(this.#icon, this.icon);
|
|
145
180
|
this.#tile.hidden = !this.icon;
|
|
146
181
|
this.#heading.textContent = this.heading;
|