@timmo001/groundwork-ui 0.9.7 → 0.9.9
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 +47 -3
- package/package.json +1 -1
- package/src/fields.ts +33 -1
- package/src/gw-alert.ts +26 -3
- package/src/gw-input.ts +22 -1
package/custom-elements.json
CHANGED
|
@@ -629,7 +629,7 @@
|
|
|
629
629
|
{
|
|
630
630
|
"kind": "variable",
|
|
631
631
|
"name": "fieldStyles",
|
|
632
|
-
"default": "css` :where(gw-input, gw-select, gw-switch, gw-checkbox) { display: flex; flex-direction: column; gap: var(--gw-space-2); font-family: var(--gw-font-sans); color: var(--gw-color-text-primary); } :where(gw-input, gw-select) .gw-field-heading { display: flex; gap: var(--gw-space-1); align-items: center; } :where(gw-input, gw-select) .gw-field-heading > label { font-size: var(--gw-font-size-s); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } /* The (i) button by the label that opens a field's longer help */ :where(gw-input, gw-select) .gw-info { display: inline-grid; place-items: center; width: 1.5rem; height: 1.5rem; padding: 0; color: var(--gw-color-text-secondary); cursor: pointer; background: none; border: 0; border-radius: var(--gw-radius-pill); transition: color var(--gw-motion-fast) var(--gw-motion-easing), background-color var(--gw-motion-fast) var(--gw-motion-easing); } :where(gw-input, gw-select) .gw-info:hover { color: var(--gw-color-text-strong); background: var(--gw-color-fill-neutral-hovered); } :where(gw-input, gw-select) .gw-info:focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :where(gw-input, gw-select) .gw-info[hidden] { display: none; } :where(gw-input, gw-select) .gw-info > .icon { width: 1rem; height: 1rem; fill: currentColor; } /* Sizes run xs to xl through --gw-field-scale, around 1 for medium, and the control's height follows the theme's density too */ :where(gw-input, gw-select)[size=\"xs\"] { --gw-field-scale: 0.6; } :where(gw-input, gw-select)[size=\"small\"] { --gw-field-scale: 0.8; } :where(gw-input, gw-select)[size=\"large\"] { --gw-field-scale: 1.2; } :where(gw-input, gw-select)[size=\"xl\"] { --gw-field-scale: 1.4; } :where(gw-input, gw-select) :where(input, .gw-select-trigger) { --gw-field-size: var(--gw-field-scale, 1); --gw-icon-size: calc( 1.25rem * var(--gw-field-size) * var(--gw-control-scale, 1) ); width: 100%; min-height: calc( 2.5rem * var(--gw-field-size) * var(--gw-control-scale, 1) ); padding: 0 calc(var(--gw-space-3) * var(--gw-field-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-field-size)); color: var(--gw-color-text-strong); background: var(--gw-color-surface-lower); border: var(--gw-border-width) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-control); transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } /* The trigger shows the chosen option, with its icon, and a chevron with room around it that turns over while the list is open */ gw-select .gw-select-trigger { display: flex; gap: calc(var(--gw-space-2) * var(--gw-field-size)); align-items: center; padding-inline-end: calc(var(--gw-space-3) * var(--gw-field-size)); text-align: start; cursor: pointer; } gw-select .gw-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* With an inline label the select sits among toolbar controls, so it's a pill that fits its content, like the toggles and buttons beside it */ gw-select[inline-label] { display: inline-flex; } gw-select[inline-label] .gw-select-trigger { width: auto; padding-inline: calc(var(--gw-space-4) * var(--gw-field-size)) calc(var(--gw-space-3) * var(--gw-field-size)); border-radius: var(--gw-radius-pill); } /* An inline label reads as a quiet prefix to the value */ gw-select .gw-select-inline-label { flex: none; color: var(--gw-color-text-secondary); } gw-select .gw-select-inline-label[hidden] { display: none; } gw-select .gw-select-value[data-placeholder] { color: var(--gw-color-text-secondary); } gw-select .gw-select-trigger > .icon { flex: none; width: var(--gw-icon-size, 1.25rem); height: var(--gw-icon-size, 1.25rem); fill: currentColor; } gw-select .gw-select-trigger > .icon:last-child { color: var(--gw-color-text-secondary); transition: rotate var(--gw-motion-normal) var(--gw-motion-easing); } gw-select[open] .gw-select-trigger > .icon:last-child { rotate: 180deg; } gw-select .gw-select-trigger > .icon[hidden] { display: none; } /* The list opens under the trigger, growing from it, or as a bottom sheet sliding up on phones */ gw-select .gw-select-popup { position: fixed; inset: unset; box-sizing: border-box; max-width: calc(100vw - 12px); padding: 0; margin: 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-panel); box-shadow: var(--gw-shadow-l); opacity: 0; scale: 1 0.96; transform-origin: top; transition: opacity var(--gw-motion-fast) var(--gw-motion-easing), scale var(--gw-motion-normal) 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; } gw-select .gw-select-popup[data-placement=\"above\"] { transform-origin: bottom; } gw-select .gw-select-popup:popover-open { opacity: 1; scale: 1; } @starting-style { gw-select .gw-select-popup:popover-open { opacity: 0; scale: 1 0.96; } } gw-select .gw-select-popup[data-placement=\"sheet\"] { inset: auto 0 0; width: 100%; max-width: none; padding-block-end: env(safe-area-inset-bottom); border-end-start-radius: 0; border-end-end-radius: 0; scale: 1; translate: 0 100%; --gw-option-list-height: 60vh; } gw-select .gw-select-popup[data-placement=\"sheet\"]:popover-open { translate: 0 0; } @starting-style { gw-select .gw-select-popup[data-placement=\"sheet\"]:popover-open { translate: 0 100%; } } gw-select .gw-select-popup[data-placement=\"sheet\"]::backdrop { background: rgb(0 0 0 / 40%); } /* With multiple, a full-width Clear under the list empties the choice */ gw-select .gw-select-clear { display: block; width: 100%; min-height: 2.5rem; padding: 0 var(--gw-space-3); font: inherit; font-size: var(--gw-font-size-s); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-secondary); cursor: pointer; background: none; border: 0; border-block-start: var(--gw-border-width) solid var(--gw-color-border-quiet); transition: color var(--gw-motion-fast) var(--gw-motion-easing), background-color var(--gw-motion-fast) var(--gw-motion-easing); } gw-select .gw-select-clear:hover { color: var(--gw-color-text-strong); background: var(--gw-color-fill-neutral-hovered); } gw-select .gw-select-clear:focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: calc(var(--gw-focus-width) * -1); } gw-select .gw-select-clear[hidden] { display: none; } :where(gw-input, gw-select) :where(input, .gw-select-trigger):hover { border-color: var(--gw-color-border-loud); } :where(gw-input, gw-select, gw-switch, gw-checkbox) :where(input, .gw-select-trigger):focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :where(gw-input, gw-select) [aria-invalid=\"true\"] { border-color: var(--gw-color-border-danger); } :where(gw-input, gw-select, gw-switch, gw-checkbox) :disabled { color: var(--gw-color-text-disabled); cursor: not-allowed; } :where(gw-input, gw-select, gw-switch, gw-checkbox) .gw-hint { margin: 0; font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } :where(gw-input, gw-select, gw-switch, gw-checkbox) .gw-error { margin: 0; font-size: var(--gw-font-size-xs); color: var(--gw-color-danger); } :where(gw-switch, gw-checkbox) > label { display: inline-flex; gap: var(--gw-space-3); align-items: center; font-size: var(--gw-font-size-s); cursor: pointer; } :where(gw-switch, gw-checkbox, gw-select) .gw-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } gw-checkbox input { width: 1.125rem; height: 1.125rem; margin: 0; accent-color: var(--gw-color-fill-accent-resting); } gw-switch input { position: relative; flex: none; width: 2.5rem; height: 1.5rem; margin: 0; cursor: pointer; appearance: none; background: var(--gw-color-surface-lower); border: var(--gw-border-width) solid var(--gw-color-border-loud); border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), border-color var(--gw-motion-normal) var(--gw-motion-easing); } gw-switch input::after { position: absolute; inset-block-start: 0.1875rem; inset-inline-start: 0.1875rem; width: 1rem; height: 1rem; content: \"\"; background: var(--gw-color-text-secondary); border-radius: 50%; transition: translate var(--gw-motion-normal) var(--gw-motion-easing), background-color var(--gw-motion-normal) var(--gw-motion-easing); } gw-switch input:checked { background: var(--gw-color-fill-accent-resting); border-color: transparent; } gw-switch input:checked::after { translate: 1rem 0; background: var(--gw-color-on-accent); } gw-switch input:disabled { opacity: 0.5; } `",
|
|
632
|
+
"default": "css` :where(gw-input, gw-select, gw-switch, gw-checkbox) { display: flex; flex-direction: column; gap: var(--gw-space-2); font-family: var(--gw-font-sans); color: var(--gw-color-text-primary); } :where(gw-input, gw-select) .gw-field-heading { display: flex; gap: var(--gw-space-1); align-items: center; } :where(gw-input, gw-select) .gw-field-heading > label { font-size: var(--gw-font-size-s); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } /* The (i) button by the label that opens a field's longer help */ :where(gw-input, gw-select) .gw-info { display: inline-grid; place-items: center; width: 1.5rem; height: 1.5rem; padding: 0; color: var(--gw-color-text-secondary); cursor: pointer; background: none; border: 0; border-radius: var(--gw-radius-pill); transition: color var(--gw-motion-fast) var(--gw-motion-easing), background-color var(--gw-motion-fast) var(--gw-motion-easing); } :where(gw-input, gw-select) .gw-info:hover { color: var(--gw-color-text-strong); background: var(--gw-color-fill-neutral-hovered); } :where(gw-input, gw-select) .gw-info:focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :where(gw-input, gw-select) .gw-info[hidden] { display: none; } :where(gw-input, gw-select) .gw-info > .icon { width: 1rem; height: 1rem; fill: currentColor; } /* Sizes run xs to xl through --gw-field-scale, around 1 for medium, and the control's height follows the theme's density too */ :where(gw-input, gw-select)[size=\"xs\"] { --gw-field-scale: 0.6; } :where(gw-input, gw-select)[size=\"small\"] { --gw-field-scale: 0.8; } :where(gw-input, gw-select)[size=\"large\"] { --gw-field-scale: 1.2; } :where(gw-input, gw-select)[size=\"xl\"] { --gw-field-scale: 1.4; } :where(gw-input, gw-select) :where(input, .gw-select-trigger) { --gw-field-size: var(--gw-field-scale, 1); --gw-icon-size: calc( 1.25rem * var(--gw-field-size) * var(--gw-control-scale, 1) ); width: 100%; min-height: calc( 2.5rem * var(--gw-field-size) * var(--gw-control-scale, 1) ); padding: 0 calc(var(--gw-space-3) * var(--gw-field-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-field-size)); color: var(--gw-color-text-strong); background: var(--gw-color-surface-lower); border: var(--gw-border-width) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-control); transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } /* An icon inside the field, before the text, with the text moved clear of it */ gw-input .gw-input-control { position: relative; display: flex; align-items: center; } gw-input .gw-input-control > .icon { position: absolute; inset-inline-start: calc(var(--gw-space-3) * var(--gw-field-scale, 1)); width: calc(1.125rem * var(--gw-field-scale, 1)); height: calc(1.125rem * var(--gw-field-scale, 1)); color: var(--gw-color-text-secondary); pointer-events: none; fill: currentColor; } gw-input .gw-input-control > .icon[hidden] { display: none; } gw-input .gw-input-control > .icon:not([hidden]) + input { padding-inline-start: calc( (var(--gw-space-3) * 2 + 1.125rem) * var(--gw-field-scale, 1) ); } /* The trigger shows the chosen option, with its icon, and a chevron with room around it that turns over while the list is open */ gw-select .gw-select-trigger { display: flex; gap: calc(var(--gw-space-2) * var(--gw-field-size)); align-items: center; padding-inline-end: calc(var(--gw-space-3) * var(--gw-field-size)); text-align: start; cursor: pointer; } gw-select .gw-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* With an inline label the select sits among toolbar controls, so it's a pill that fits its content, like the toggles and buttons beside it */ gw-select[inline-label] { display: inline-flex; } gw-select[inline-label] .gw-select-trigger { width: auto; padding-inline: calc(var(--gw-space-4) * var(--gw-field-size)) calc(var(--gw-space-3) * var(--gw-field-size)); border-radius: var(--gw-radius-pill); } /* An inline label reads as a quiet prefix to the value */ gw-select .gw-select-inline-label { flex: none; color: var(--gw-color-text-secondary); } gw-select .gw-select-inline-label[hidden] { display: none; } gw-select .gw-select-value[data-placeholder] { color: var(--gw-color-text-secondary); } gw-select .gw-select-trigger > .icon { flex: none; width: var(--gw-icon-size, 1.25rem); height: var(--gw-icon-size, 1.25rem); fill: currentColor; } gw-select .gw-select-trigger > .icon:last-child { color: var(--gw-color-text-secondary); transition: rotate var(--gw-motion-normal) var(--gw-motion-easing); } gw-select[open] .gw-select-trigger > .icon:last-child { rotate: 180deg; } gw-select .gw-select-trigger > .icon[hidden] { display: none; } /* The list opens under the trigger, growing from it, or as a bottom sheet sliding up on phones */ gw-select .gw-select-popup { position: fixed; inset: unset; box-sizing: border-box; max-width: calc(100vw - 12px); padding: 0; margin: 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-panel); box-shadow: var(--gw-shadow-l); opacity: 0; scale: 1 0.96; transform-origin: top; transition: opacity var(--gw-motion-fast) var(--gw-motion-easing), scale var(--gw-motion-normal) 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; } gw-select .gw-select-popup[data-placement=\"above\"] { transform-origin: bottom; } gw-select .gw-select-popup:popover-open { opacity: 1; scale: 1; } @starting-style { gw-select .gw-select-popup:popover-open { opacity: 0; scale: 1 0.96; } } gw-select .gw-select-popup[data-placement=\"sheet\"] { inset: auto 0 0; width: 100%; max-width: none; padding-block-end: env(safe-area-inset-bottom); border-end-start-radius: 0; border-end-end-radius: 0; scale: 1; translate: 0 100%; --gw-option-list-height: 60vh; } gw-select .gw-select-popup[data-placement=\"sheet\"]:popover-open { translate: 0 0; } @starting-style { gw-select .gw-select-popup[data-placement=\"sheet\"]:popover-open { translate: 0 100%; } } gw-select .gw-select-popup[data-placement=\"sheet\"]::backdrop { background: rgb(0 0 0 / 40%); } /* With multiple, a full-width Clear under the list empties the choice */ gw-select .gw-select-clear { display: block; width: 100%; min-height: 2.5rem; padding: 0 var(--gw-space-3); font: inherit; font-size: var(--gw-font-size-s); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-secondary); cursor: pointer; background: none; border: 0; border-block-start: var(--gw-border-width) solid var(--gw-color-border-quiet); transition: color var(--gw-motion-fast) var(--gw-motion-easing), background-color var(--gw-motion-fast) var(--gw-motion-easing); } gw-select .gw-select-clear:hover { color: var(--gw-color-text-strong); background: var(--gw-color-fill-neutral-hovered); } gw-select .gw-select-clear:focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: calc(var(--gw-focus-width) * -1); } gw-select .gw-select-clear[hidden] { display: none; } :where(gw-input) .gw-input-control:focus-within > .icon { color: var(--gw-color-text-strong); } :where(gw-input, gw-select) :where(input, .gw-select-trigger):hover { border-color: var(--gw-color-border-loud); } :where(gw-input, gw-select, gw-switch, gw-checkbox) :where(input, .gw-select-trigger):focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :where(gw-input, gw-select) [aria-invalid=\"true\"] { border-color: var(--gw-color-border-danger); } :where(gw-input, gw-select, gw-switch, gw-checkbox) :disabled { color: var(--gw-color-text-disabled); cursor: not-allowed; } :where(gw-input, gw-select, gw-switch, gw-checkbox) .gw-hint { margin: 0; font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } :where(gw-input, gw-select, gw-switch, gw-checkbox) .gw-error { margin: 0; font-size: var(--gw-font-size-xs); color: var(--gw-color-danger); } :where(gw-switch, gw-checkbox) > label { display: inline-flex; gap: var(--gw-space-3); align-items: center; font-size: var(--gw-font-size-s); cursor: pointer; } :where(gw-input, gw-switch, gw-checkbox, gw-select) .gw-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } gw-checkbox input { width: 1.125rem; height: 1.125rem; margin: 0; accent-color: var(--gw-color-fill-accent-resting); } gw-switch input { position: relative; flex: none; width: 2.5rem; height: 1.5rem; margin: 0; cursor: pointer; appearance: none; background: var(--gw-color-surface-lower); border: var(--gw-border-width) solid var(--gw-color-border-loud); border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), border-color var(--gw-motion-normal) var(--gw-motion-easing); } gw-switch input::after { position: absolute; inset-block-start: 0.1875rem; inset-inline-start: 0.1875rem; width: 1rem; height: 1rem; content: \"\"; background: var(--gw-color-text-secondary); border-radius: 50%; transition: translate var(--gw-motion-normal) var(--gw-motion-easing), background-color var(--gw-motion-normal) var(--gw-motion-easing); } gw-switch input:checked { background: var(--gw-color-fill-accent-resting); border-color: transparent; } gw-switch input:checked::after { translate: 1rem 0; background: var(--gw-color-on-accent); } gw-switch input:disabled { opacity: 0.5; } `",
|
|
633
633
|
"description": "Field components render their native control in light DOM, never in a\nshadow root: password managers and `<label for>` can't reach across one,\nand a form only submits controls it can see. Their styles are adopted into\nwhichever document or shadow root they sit in."
|
|
634
634
|
},
|
|
635
635
|
{
|
|
@@ -1076,7 +1076,7 @@
|
|
|
1076
1076
|
"text": "readonly CSSStyleSheet[]"
|
|
1077
1077
|
},
|
|
1078
1078
|
"static": true,
|
|
1079
|
-
"default": "[ base, css` :host { display: block; } .alert { display: flex; gap: var(--gw-space-2); align-items: flex-start; padding: var(--gw-space-2) var(--gw-space-3); font-size: var(--gw-font-size-s); color: var(--gw-color-text-primary); background: var(--gw-tone-fill, var(--gw-color-fill-info-quiet)); border: var(--gw-border-width) solid var(--gw-tone-border, var(--gw-color-border-info)); border-inline-start-width: calc(var(--gw-border-width) * 4); border-radius: var(--gw-radius-control); } ::slotted(p) { margin: 0; } ::slotted([slot=\"actions\"]) { align-self: center; } ::slotted([slot=\"icon\"]) { color: var(--gw-tone, var(--gw-color-info)); } .body { flex: 1; min-width: 0; } strong { display: block; font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } `, ]",
|
|
1079
|
+
"default": "[ base, css` :host { display: block; } .alert { display: flex; gap: var(--gw-space-2); align-items: flex-start; padding: var(--gw-space-2) var(--gw-space-3); font-size: var(--gw-font-size-s); color: var(--gw-color-text-primary); background: var(--gw-tone-fill, var(--gw-color-fill-info-quiet)); border: var(--gw-border-width) solid var(--gw-tone-border, var(--gw-color-border-info)); border-inline-start-width: calc(var(--gw-border-width) * 4); border-radius: var(--gw-radius-control); } ::slotted(p) { margin: 0; } ::slotted([slot=\"actions\"]) { align-self: center; } ::slotted([slot=\"icon\"]), .icon { flex: none; color: var(--gw-tone, var(--gw-color-info)); } .body { flex: 1; min-width: 0; } strong { display: block; font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } `, ]",
|
|
1080
1080
|
"inheritedFrom": {
|
|
1081
1081
|
"name": "GwElement",
|
|
1082
1082
|
"module": "src/element.ts"
|
|
@@ -1118,6 +1118,12 @@
|
|
|
1118
1118
|
"privacy": "private",
|
|
1119
1119
|
"readonly": true
|
|
1120
1120
|
},
|
|
1121
|
+
{
|
|
1122
|
+
"kind": "field",
|
|
1123
|
+
"name": "#icon",
|
|
1124
|
+
"privacy": "private",
|
|
1125
|
+
"readonly": true
|
|
1126
|
+
},
|
|
1121
1127
|
{
|
|
1122
1128
|
"kind": "method",
|
|
1123
1129
|
"name": "create",
|
|
@@ -9765,7 +9771,7 @@
|
|
|
9765
9771
|
"text": "Properties"
|
|
9766
9772
|
},
|
|
9767
9773
|
"static": true,
|
|
9768
|
-
"default": "{ label: {}, name: {}, type: {}, value: {}, placeholder: {}, autocomplete: {}, hint: {}, info: {}, error: {}, required: { type: \"boolean\" }, disabled: { type: \"boolean\" }, size: { reflect: true }, }",
|
|
9774
|
+
"default": "{ label: {}, name: {}, type: {}, value: {}, placeholder: {}, autocomplete: {}, hint: {}, info: {}, error: {}, required: { type: \"boolean\" }, disabled: { type: \"boolean\" }, size: { reflect: true }, icon: {}, hideLabel: { type: \"boolean\", attribute: \"hide-label\", reflect: true }, }",
|
|
9769
9775
|
"inheritedFrom": {
|
|
9770
9776
|
"name": "GwElement",
|
|
9771
9777
|
"module": "src/element.ts"
|
|
@@ -9868,12 +9874,36 @@
|
|
|
9868
9874
|
},
|
|
9869
9875
|
"default": "\"medium\""
|
|
9870
9876
|
},
|
|
9877
|
+
{
|
|
9878
|
+
"kind": "field",
|
|
9879
|
+
"name": "icon",
|
|
9880
|
+
"type": {
|
|
9881
|
+
"text": "string"
|
|
9882
|
+
},
|
|
9883
|
+
"description": "SVG path data for an icon inside the field, before the text.",
|
|
9884
|
+
"default": "\"\""
|
|
9885
|
+
},
|
|
9886
|
+
{
|
|
9887
|
+
"kind": "field",
|
|
9888
|
+
"name": "hideLabel",
|
|
9889
|
+
"type": {
|
|
9890
|
+
"text": "boolean"
|
|
9891
|
+
},
|
|
9892
|
+
"description": "Keeps the label as the accessible name without showing it.",
|
|
9893
|
+
"default": "false"
|
|
9894
|
+
},
|
|
9871
9895
|
{
|
|
9872
9896
|
"kind": "field",
|
|
9873
9897
|
"name": "#id",
|
|
9874
9898
|
"privacy": "private",
|
|
9875
9899
|
"readonly": true
|
|
9876
9900
|
},
|
|
9901
|
+
{
|
|
9902
|
+
"kind": "field",
|
|
9903
|
+
"name": "#icon",
|
|
9904
|
+
"privacy": "private",
|
|
9905
|
+
"readonly": true
|
|
9906
|
+
},
|
|
9877
9907
|
{
|
|
9878
9908
|
"kind": "field",
|
|
9879
9909
|
"name": "#label",
|
|
@@ -10314,6 +10344,20 @@
|
|
|
10314
10344
|
},
|
|
10315
10345
|
"description": "The field's size, 40px tall at medium. Defaults to `medium`.",
|
|
10316
10346
|
"name": "size"
|
|
10347
|
+
},
|
|
10348
|
+
{
|
|
10349
|
+
"type": {
|
|
10350
|
+
"text": "string"
|
|
10351
|
+
},
|
|
10352
|
+
"description": "SVG path data for an icon inside the field, before the text, such as a magnifier on a search.",
|
|
10353
|
+
"name": "icon"
|
|
10354
|
+
},
|
|
10355
|
+
{
|
|
10356
|
+
"type": {
|
|
10357
|
+
"text": "boolean"
|
|
10358
|
+
},
|
|
10359
|
+
"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.",
|
|
10360
|
+
"name": "hide-label"
|
|
10317
10361
|
}
|
|
10318
10362
|
],
|
|
10319
10363
|
"superclass": {
|
package/package.json
CHANGED
package/src/fields.ts
CHANGED
|
@@ -108,6 +108,34 @@ export const fieldStyles = css`
|
|
|
108
108
|
transition: border-color var(--gw-motion-fast) var(--gw-motion-easing);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/* An icon inside the field, before the text, with the text moved clear of
|
|
112
|
+
it */
|
|
113
|
+
gw-input .gw-input-control {
|
|
114
|
+
position: relative;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
gw-input .gw-input-control > .icon {
|
|
120
|
+
position: absolute;
|
|
121
|
+
inset-inline-start: calc(var(--gw-space-3) * var(--gw-field-scale, 1));
|
|
122
|
+
width: calc(1.125rem * var(--gw-field-scale, 1));
|
|
123
|
+
height: calc(1.125rem * var(--gw-field-scale, 1));
|
|
124
|
+
color: var(--gw-color-text-secondary);
|
|
125
|
+
pointer-events: none;
|
|
126
|
+
fill: currentColor;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
gw-input .gw-input-control > .icon[hidden] {
|
|
130
|
+
display: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
gw-input .gw-input-control > .icon:not([hidden]) + input {
|
|
134
|
+
padding-inline-start: calc(
|
|
135
|
+
(var(--gw-space-3) * 2 + 1.125rem) * var(--gw-field-scale, 1)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
111
139
|
/* The trigger shows the chosen option, with its icon, and a chevron with
|
|
112
140
|
room around it that turns over while the list is open */
|
|
113
141
|
gw-select .gw-select-trigger {
|
|
@@ -276,6 +304,10 @@ export const fieldStyles = css`
|
|
|
276
304
|
display: none;
|
|
277
305
|
}
|
|
278
306
|
|
|
307
|
+
:where(gw-input) .gw-input-control:focus-within > .icon {
|
|
308
|
+
color: var(--gw-color-text-strong);
|
|
309
|
+
}
|
|
310
|
+
|
|
279
311
|
:where(gw-input, gw-select) :where(input, .gw-select-trigger):hover {
|
|
280
312
|
border-color: var(--gw-color-border-loud);
|
|
281
313
|
}
|
|
@@ -315,7 +347,7 @@ export const fieldStyles = css`
|
|
|
315
347
|
cursor: pointer;
|
|
316
348
|
}
|
|
317
349
|
|
|
318
|
-
:where(gw-switch, gw-checkbox, gw-select) .gw-visually-hidden {
|
|
350
|
+
:where(gw-input, gw-switch, gw-checkbox, gw-select) .gw-visually-hidden {
|
|
319
351
|
position: absolute;
|
|
320
352
|
width: 1px;
|
|
321
353
|
height: 1px;
|
package/src/gw-alert.ts
CHANGED
|
@@ -5,7 +5,24 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { GwElement, css, h } from "./element.js";
|
|
8
|
-
import { base, type Tone } from "./styles.js";
|
|
8
|
+
import { base, createIcon, setIconPath, type Tone } from "./styles.js";
|
|
9
|
+
|
|
10
|
+
// mdi's outlined information, check, alert and alert-decagram icons
|
|
11
|
+
const information =
|
|
12
|
+
"M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z";
|
|
13
|
+
|
|
14
|
+
const toneIcons: Readonly<Record<Tone, string>> = {
|
|
15
|
+
neutral: information,
|
|
16
|
+
accent: information,
|
|
17
|
+
info: information,
|
|
18
|
+
success:
|
|
19
|
+
"M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M12 20C7.59 20 4 16.41 4 12S7.59 4 12 4 20 7.59 20 12 16.41 20 12 20M16.59 7.58L10 14.17L7.41 11.59L6 13L10 17L18 9L16.59 7.58Z",
|
|
20
|
+
warning: "M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16",
|
|
21
|
+
danger:
|
|
22
|
+
"M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z",
|
|
23
|
+
important:
|
|
24
|
+
"M23,12L20.56,14.78L20.9,18.46L17.29,19.28L15.4,22.46L12,21L8.6,22.47L6.71,19.29L3.1,18.47L3.44,14.78L1,12L3.44,9.21L3.1,5.53L6.71,4.72L8.6,1.54L12,3L15.4,1.54L17.29,4.72L20.9,5.54L20.56,9.22L23,12M20.33,12L18.5,9.89L18.74,7.1L16,6.5L14.58,4.07L12,5.18L9.42,4.07L8,6.5L5.26,7.09L5.5,9.88L3.67,12L5.5,14.1L5.26,16.9L8,17.5L9.42,19.93L12,18.81L14.58,19.92L16,17.5L18.74,16.89L18.5,14.1L20.33,12M11,15H13V17H11V15M11,7H13V13H11V7",
|
|
25
|
+
};
|
|
9
26
|
|
|
10
27
|
/**
|
|
11
28
|
* A message in the flow of the page. `urgent` announces it at once, for
|
|
@@ -64,7 +81,9 @@ export class GwAlert extends GwElement {
|
|
|
64
81
|
align-self: center;
|
|
65
82
|
}
|
|
66
83
|
|
|
67
|
-
::slotted([slot="icon"])
|
|
84
|
+
::slotted([slot="icon"]),
|
|
85
|
+
.icon {
|
|
86
|
+
flex: none;
|
|
68
87
|
color: var(--gw-tone, var(--gw-color-info));
|
|
69
88
|
}
|
|
70
89
|
|
|
@@ -89,6 +108,9 @@ export class GwAlert extends GwElement {
|
|
|
89
108
|
|
|
90
109
|
readonly #heading = h("strong");
|
|
91
110
|
|
|
111
|
+
// The tone's icon, shown until an icon is slotted in its place
|
|
112
|
+
readonly #icon = createIcon("");
|
|
113
|
+
|
|
92
114
|
constructor() {
|
|
93
115
|
super();
|
|
94
116
|
this.tone = "info";
|
|
@@ -98,7 +120,7 @@ export class GwAlert extends GwElement {
|
|
|
98
120
|
|
|
99
121
|
protected override create(root: ShadowRoot) {
|
|
100
122
|
this.#alert.append(
|
|
101
|
-
h("slot", { name: "icon" }),
|
|
123
|
+
h("slot", { name: "icon" }, this.#icon),
|
|
102
124
|
h("div", { class: "body" }, this.#heading, h("slot")),
|
|
103
125
|
h("slot", { name: "actions" }),
|
|
104
126
|
);
|
|
@@ -107,6 +129,7 @@ export class GwAlert extends GwElement {
|
|
|
107
129
|
|
|
108
130
|
protected override update() {
|
|
109
131
|
this.#alert.setAttribute("role", this.urgent ? "alert" : "status");
|
|
132
|
+
setIconPath(this.#icon, toneIcons[this.tone] ?? information);
|
|
110
133
|
this.#heading.textContent = this.heading;
|
|
111
134
|
this.#heading.hidden = !this.heading;
|
|
112
135
|
}
|
package/src/gw-input.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { h, setAttribute } from "./element.js";
|
|
8
|
+
import { createIcon, setIconPath } from "./styles.js";
|
|
8
9
|
import { FieldInfo } from "./field-info.js";
|
|
9
10
|
import { LightElement, fieldId } from "./fields.js";
|
|
10
11
|
|
|
@@ -26,6 +27,8 @@ import { LightElement, fieldId } from "./fields.js";
|
|
|
26
27
|
* @attr {boolean} required - Requires a value.
|
|
27
28
|
* @attr {boolean} disabled - Disables the field.
|
|
28
29
|
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The field's size, 40px tall at medium. Defaults to `medium`.
|
|
30
|
+
* @attr {string} icon - SVG path data for an icon inside the field, before the text, such as a magnifier on a search.
|
|
31
|
+
* @attr {boolean} hide-label - 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.
|
|
29
32
|
*
|
|
30
33
|
* @cssprop --gw-field-scale - Scales the field, around 1 for medium.
|
|
31
34
|
* @cssprop --gw-control-scale - Scales its height with the theme's density. The theme sets it.
|
|
@@ -44,6 +47,8 @@ export class GwInput extends LightElement {
|
|
|
44
47
|
required: { type: "boolean" },
|
|
45
48
|
disabled: { type: "boolean" },
|
|
46
49
|
size: { reflect: true },
|
|
50
|
+
icon: {},
|
|
51
|
+
hideLabel: { type: "boolean", attribute: "hide-label", reflect: true },
|
|
47
52
|
} as const;
|
|
48
53
|
|
|
49
54
|
declare label: string;
|
|
@@ -59,9 +64,15 @@ export class GwInput extends LightElement {
|
|
|
59
64
|
declare required: boolean;
|
|
60
65
|
declare disabled: boolean;
|
|
61
66
|
declare size: "xs" | "small" | "medium" | "large" | "xl";
|
|
67
|
+
/** SVG path data for an icon inside the field, before the text. */
|
|
68
|
+
declare icon: string;
|
|
69
|
+
/** Keeps the label as the accessible name without showing it. */
|
|
70
|
+
declare hideLabel: boolean;
|
|
62
71
|
|
|
63
72
|
readonly #id = fieldId();
|
|
64
73
|
|
|
74
|
+
readonly #icon = createIcon("");
|
|
75
|
+
|
|
65
76
|
readonly #label = h("label", { for: this.#id });
|
|
66
77
|
|
|
67
78
|
readonly #info = new FieldInfo(this.#label);
|
|
@@ -86,13 +97,20 @@ export class GwInput extends LightElement {
|
|
|
86
97
|
this.required = false;
|
|
87
98
|
this.disabled = false;
|
|
88
99
|
this.size = "medium";
|
|
100
|
+
this.icon = "";
|
|
101
|
+
this.hideLabel = false;
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
protected override create(root: HTMLElement) {
|
|
92
105
|
this.#input.addEventListener("input", () => {
|
|
93
106
|
this.value = this.#input.value;
|
|
94
107
|
});
|
|
95
|
-
root.append(
|
|
108
|
+
root.append(
|
|
109
|
+
this.#info.heading,
|
|
110
|
+
h("span", { class: "gw-input-control" }, this.#icon, this.#input),
|
|
111
|
+
this.#hint,
|
|
112
|
+
this.#error,
|
|
113
|
+
);
|
|
96
114
|
}
|
|
97
115
|
|
|
98
116
|
protected override update() {
|
|
@@ -107,6 +125,9 @@ export class GwInput extends LightElement {
|
|
|
107
125
|
|
|
108
126
|
this.#label.textContent = this.label;
|
|
109
127
|
this.#info.update(this.label, this.info);
|
|
128
|
+
this.#info.heading.classList.toggle("gw-visually-hidden", this.hideLabel);
|
|
129
|
+
setIconPath(this.#icon, this.icon);
|
|
130
|
+
this.#icon.toggleAttribute("hidden", !this.icon);
|
|
110
131
|
input.type = this.type;
|
|
111
132
|
setAttribute(input, "name", this.name || null);
|
|
112
133
|
setAttribute(input, "placeholder", this.placeholder || null);
|