@timmo001/groundwork-ui 0.9.8 → 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 +40 -2
- package/package.json +1 -1
- package/src/fields.ts +33 -1
- 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
|
{
|
|
@@ -9771,7 +9771,7 @@
|
|
|
9771
9771
|
"text": "Properties"
|
|
9772
9772
|
},
|
|
9773
9773
|
"static": true,
|
|
9774
|
-
"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 }, }",
|
|
9775
9775
|
"inheritedFrom": {
|
|
9776
9776
|
"name": "GwElement",
|
|
9777
9777
|
"module": "src/element.ts"
|
|
@@ -9874,12 +9874,36 @@
|
|
|
9874
9874
|
},
|
|
9875
9875
|
"default": "\"medium\""
|
|
9876
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
|
+
},
|
|
9877
9895
|
{
|
|
9878
9896
|
"kind": "field",
|
|
9879
9897
|
"name": "#id",
|
|
9880
9898
|
"privacy": "private",
|
|
9881
9899
|
"readonly": true
|
|
9882
9900
|
},
|
|
9901
|
+
{
|
|
9902
|
+
"kind": "field",
|
|
9903
|
+
"name": "#icon",
|
|
9904
|
+
"privacy": "private",
|
|
9905
|
+
"readonly": true
|
|
9906
|
+
},
|
|
9883
9907
|
{
|
|
9884
9908
|
"kind": "field",
|
|
9885
9909
|
"name": "#label",
|
|
@@ -10320,6 +10344,20 @@
|
|
|
10320
10344
|
},
|
|
10321
10345
|
"description": "The field's size, 40px tall at medium. Defaults to `medium`.",
|
|
10322
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"
|
|
10323
10361
|
}
|
|
10324
10362
|
],
|
|
10325
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-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);
|