@rak200/ui 0.2.10 → 0.2.11

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/README.md CHANGED
@@ -34,6 +34,16 @@ npm install @rak200/ui
34
34
  <span slot="help">In BRL, two decimals.</span>
35
35
  </ui-field>
36
36
 
37
+ <ui-field>
38
+ <label slot="label">Currency</label>
39
+ <ui-select>
40
+ <select name="currency">
41
+ <option value="brl">Real</option>
42
+ <option value="usd">Dollar</option>
43
+ </select>
44
+ </ui-select>
45
+ </ui-field>
46
+
37
47
  <ui-field>
38
48
  <label slot="label">Email notifications</label>
39
49
  <ui-switch><input type="checkbox" name="notify" checked /></ui-switch>
@@ -76,8 +86,8 @@ components; `docs/` is what describes them.
76
86
 
77
87
  ## Status
78
88
 
79
- **v0.** Seven components — `<ui-button>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
80
- `<ui-textarea>`, `<ui-checkbox>` and `<ui-switch>` — and the token layer under them, built to the ecosystem's full quality bar rather than sketched: type-checked at the strictest available
89
+ **v0.** Eight components — `<ui-button>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
90
+ `<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>` and `<ui-select>` — and the token layer under them, built to the ecosystem's full quality bar rather than sketched: type-checked at the strictest available
81
91
  setting, formatted, tested in a real browser and **asserted against axe** for WCAG A/AA, 100%
82
92
  coverage and **100% mutation score**, scanned, and every public symbol documented. The v0 surface in
83
93
  RFC 0016 grows from here — see [ROADMAP.md](ROADMAP.md).
package/dist/index.d.ts CHANGED
@@ -3,5 +3,6 @@ export { UiCheckbox, UiSwitch } from './checkbox.js';
3
3
  export { UiDialog } from './dialog.js';
4
4
  export { UiField } from './field.js';
5
5
  export { UiInput, UiTextarea } from './input.js';
6
+ export { UiSelect } from './select.js';
6
7
  export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, type Token, type DerivedToken, } from './tokens.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -3,5 +3,6 @@ export { UiCheckbox, UiSwitch } from './checkbox.js';
3
3
  export { UiDialog } from './dialog.js';
4
4
  export { UiField } from './field.js';
5
5
  export { UiInput, UiTextarea } from './input.js';
6
+ export { UiSelect } from './select.js';
6
7
  export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, } from './tokens.js';
7
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { LitElement, type CSSResult, type TemplateResult } from 'lit';
2
+ /**
3
+ * A native `<select>`, styled by the token layer rather than replaced.
4
+ *
5
+ * **The `<select>` is yours.** You write it and its `<option>`s, and it stays in the light
6
+ * DOM — the same shape {@link UiInput} has, and forced by the same constraint: an ARIA
7
+ * relationship by IDREF does not cross a shadow boundary, so a control rendered in here
8
+ * could not be labelled by the `<label>` beside it. It reaches a form submit because it is
9
+ * a native control inside a `<form>`.
10
+ *
11
+ * **The native element is the decision, not a shortcut.** A custom listbox is an
12
+ * accessibility project of its own, and it would have to reimplement the platform picker
13
+ * a phone already opens — which is the part a consumer notices most and a library gets
14
+ * wrong most. RFC 0016 defers that listbox, and this element is not a step toward it.
15
+ *
16
+ * **What the platform still refuses is documented rather than worked around**, in
17
+ * `docs/select.md`: the drop-down list is drawn by the operating system and no rule here
18
+ * reaches inside it.
19
+ *
20
+ * ## Why the box is written out rather than shared
21
+ *
22
+ * `src/input.ts` carries the same box and says that duplicating it would be *two copies of
23
+ * a contract with nothing comparing them*. That objection is answered on its own terms
24
+ * rather than dodged: `tests/select.test.ts` mounts both and asserts they agree on the
25
+ * boundary, the radius, the padding, the font and the focus ring. Something compares them,
26
+ * and it fails when they drift.
27
+ *
28
+ * Sharing was the alternative and it costs the thing this file exists for — a consumer
29
+ * looking for `<ui-select>` finds `select.ts`, `select.stories.ts`, `select.test.ts` and
30
+ * `select.md`, rather than a select folded into the input's page under the input's name.
31
+ *
32
+ * @example
33
+ * ```html
34
+ * <ui-field>
35
+ * <label slot="label">Currency</label>
36
+ * <ui-select>
37
+ * <select name="currency">
38
+ * <option value="brl">Real</option>
39
+ * <option value="usd">Dollar</option>
40
+ * </select>
41
+ * </ui-select>
42
+ * </ui-field>
43
+ * ```
44
+ */
45
+ export declare class UiSelect extends LitElement {
46
+ static readonly styles: CSSResult;
47
+ render(): TemplateResult;
48
+ }
49
+ declare global {
50
+ interface HTMLElementTagNameMap {
51
+ 'ui-select': UiSelect;
52
+ }
53
+ }
54
+ //# sourceMappingURL=select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAqBjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBAAa,QAAS,SAAQ,UAAU;IACpC,gBAAyB,MAAM,EAAE,SAAS,CA8FxC;IAEO,MAAM,IAAI,cAAc;CAGpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,WAAW,EAAE,QAAQ,CAAC;KACzB;CACJ"}
package/dist/select.js ADDED
@@ -0,0 +1,167 @@
1
+ import { LitElement, css, html } from 'lit';
2
+ import { reference } from './reference.js';
3
+ /**
4
+ * One arm of the caret, and the gap it keeps from the edge.
5
+ *
6
+ * A fraction of the space token rather than a literal, so the mark scales with everything
7
+ * else a host retunes — and small enough that the two arms plus their gap fit inside the
8
+ * padding reserved for them below.
9
+ */
10
+ const arm = css `calc(${reference('--ui-space')} * 0.75)`;
11
+ /**
12
+ * The room the caret needs, which the text must not run into.
13
+ *
14
+ * The caret ends one space from the edge and is two arms wide, so this is that plus one
15
+ * more space of air. Written as the sum rather than as a number, because a number would be
16
+ * right at the default space token and wrong at every other.
17
+ */
18
+ const clearance = css `calc(${reference('--ui-space')} * 2 + ${arm} * 2)`;
19
+ /**
20
+ * A native `<select>`, styled by the token layer rather than replaced.
21
+ *
22
+ * **The `<select>` is yours.** You write it and its `<option>`s, and it stays in the light
23
+ * DOM — the same shape {@link UiInput} has, and forced by the same constraint: an ARIA
24
+ * relationship by IDREF does not cross a shadow boundary, so a control rendered in here
25
+ * could not be labelled by the `<label>` beside it. It reaches a form submit because it is
26
+ * a native control inside a `<form>`.
27
+ *
28
+ * **The native element is the decision, not a shortcut.** A custom listbox is an
29
+ * accessibility project of its own, and it would have to reimplement the platform picker
30
+ * a phone already opens — which is the part a consumer notices most and a library gets
31
+ * wrong most. RFC 0016 defers that listbox, and this element is not a step toward it.
32
+ *
33
+ * **What the platform still refuses is documented rather than worked around**, in
34
+ * `docs/select.md`: the drop-down list is drawn by the operating system and no rule here
35
+ * reaches inside it.
36
+ *
37
+ * ## Why the box is written out rather than shared
38
+ *
39
+ * `src/input.ts` carries the same box and says that duplicating it would be *two copies of
40
+ * a contract with nothing comparing them*. That objection is answered on its own terms
41
+ * rather than dodged: `tests/select.test.ts` mounts both and asserts they agree on the
42
+ * boundary, the radius, the padding, the font and the focus ring. Something compares them,
43
+ * and it fails when they drift.
44
+ *
45
+ * Sharing was the alternative and it costs the thing this file exists for — a consumer
46
+ * looking for `<ui-select>` finds `select.ts`, `select.stories.ts`, `select.test.ts` and
47
+ * `select.md`, rather than a select folded into the input's page under the input's name.
48
+ *
49
+ * @example
50
+ * ```html
51
+ * <ui-field>
52
+ * <label slot="label">Currency</label>
53
+ * <ui-select>
54
+ * <select name="currency">
55
+ * <option value="brl">Real</option>
56
+ * <option value="usd">Dollar</option>
57
+ * </select>
58
+ * </ui-select>
59
+ * </ui-field>
60
+ * ```
61
+ */
62
+ export class UiSelect extends LitElement {
63
+ static { this.styles = css `
64
+ :host {
65
+ display: block;
66
+ }
67
+
68
+ /* The box, which src/input.ts also draws and tests/select.test.ts compares. */
69
+ ::slotted(select) {
70
+ box-sizing: border-box;
71
+ inline-size: 100%;
72
+ font: inherit;
73
+ font-family: ${reference('--ui-font')};
74
+ color: ${reference('--ui-color-text')};
75
+ background-color: ${reference('--ui-color-surface')};
76
+ border: 1px solid ${reference('--ui-color-border')};
77
+ border-radius: ${reference('--ui-radius')};
78
+ padding: ${reference('--ui-space')};
79
+ /* A select opens something when clicked, so it is a pointer rather than a
80
+ caret — the one place this box disagrees with the input's on purpose. */
81
+ cursor: pointer;
82
+ /* Only the boundary moves. The focus ring is deliberately not in this list,
83
+ for the reason src/button.ts gives beside its own. */
84
+ transition: border-color ${reference('--ui-duration-state')}
85
+ ${reference('--ui-easing-state')};
86
+ }
87
+
88
+ /* Taking the platform's own drawing off is what makes the box possible at all: a
89
+ select left at appearance: auto keeps the operating system's chevron and, off
90
+ this engine, ignores much of what is set above — so the control beside a text
91
+ field would stop matching it exactly where a kit is supposed to. What that
92
+ costs is the chevron, which is drawn below.
93
+
94
+ It does NOT cost the picker. appearance: none changes how the closed control
95
+ is painted and nothing about what opens, so a phone still opens its own wheel. */
96
+ ::slotted(select) {
97
+ appearance: none;
98
+ padding-inline-end: ${clearance};
99
+ background-repeat: no-repeat;
100
+ }
101
+
102
+ /* The caret is two triangles rather than a picture, and that is what keeps its
103
+ colour a token: gradients take var(), and an SVG in a data: URI would
104
+ freeze whatever colour was drawn into it. src/checkbox.ts solved the same
105
+ problem the other way, by making its mark a hole — which is not available here,
106
+ because a mask would clip the option text with it.
107
+
108
+ A multiple select is a list rather than a drop-down, and a caret on a list
109
+ points at nothing, so it is guarded rather than drawn everywhere. */
110
+ ::slotted(select:not([multiple])) {
111
+ background-image:
112
+ linear-gradient(45deg, transparent 50%, ${reference('--ui-color-text-muted')} 50%),
113
+ linear-gradient(135deg, ${reference('--ui-color-text-muted')} 50%, transparent 50%);
114
+ background-size: ${arm} ${arm};
115
+ background-position:
116
+ right calc(${reference('--ui-space')} + ${arm}) center,
117
+ right ${reference('--ui-space')} center;
118
+ }
119
+
120
+ /* Direction is the control's own, not the page's, and background-position has no
121
+ logical form — so the one physical thing in this sheet is mirrored explicitly.
122
+ The padding needs no rule: padding-inline-end already follows. Measured, both
123
+ halves. */
124
+ ::slotted(select:not([multiple]):dir(rtl)) {
125
+ background-position:
126
+ left ${reference('--ui-space')} center,
127
+ left calc(${reference('--ui-space')} + ${arm}) center;
128
+ }
129
+
130
+ /* The boundary completes the mix it already started, the same way ui-input's does
131
+ and against the same token.
132
+
133
+ There is no [readonly] guard here and its absence is measured rather than
134
+ forgotten: readOnly is not a property of a select at all, so the input's
135
+ second guard would be a rule about an attribute the platform never sets. */
136
+ ::slotted(select:hover:not(:disabled)) {
137
+ border-color: ${reference('--ui-color-text')};
138
+ }
139
+
140
+ /* A visible focus ring is not decoration: removing it is the single most common
141
+ way a component stops being usable by keyboard. */
142
+ ::slotted(select:focus-visible) {
143
+ outline: 2px solid ${reference('--ui-color-focus')};
144
+ outline-offset: 2px;
145
+ }
146
+
147
+ ::slotted(select:disabled) {
148
+ cursor: not-allowed;
149
+ opacity: 0.5;
150
+ }
151
+
152
+ /* The error state is not this component's to decide: ui-field sets aria-invalid on
153
+ the control as part of the wiring it already owns, and this rule reads it. */
154
+ ::slotted(select[aria-invalid='true']) {
155
+ border-color: ${reference('--ui-color-danger')};
156
+ }
157
+ `; }
158
+ render() {
159
+ return html `<slot></slot>`;
160
+ }
161
+ }
162
+ // Stryker disable next-line StringLiteral: the registration runs once, at import, inside
163
+ // the warm process Stryker switches mutants in — so by the time a mutant on this line is
164
+ // active the element is already defined under the original name, and no test can observe
165
+ // the change. Outside the runner's reach, not an equivalent mutant.
166
+ customElements.define('ui-select', UiSelect);
167
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAuC,MAAM,KAAK,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,GAAG,GAAG,GAAG,CAAA,QAAQ,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,GAAG,CAAA,QAAQ,SAAS,CAAC,YAAY,CAAC,UAAU,GAAG,OAAO,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,OAAO,QAAS,SAAQ,UAAU;aACX,WAAM,GAAc,GAAG,CAAA;;;;;;;;;;2BAUzB,SAAS,CAAC,WAAW,CAAC;qBAC5B,SAAS,CAAC,iBAAiB,CAAC;gCACjB,SAAS,CAAC,oBAAoB,CAAC;gCAC/B,SAAS,CAAC,mBAAmB,CAAC;6BACjC,SAAS,CAAC,aAAa,CAAC;uBAC9B,SAAS,CAAC,YAAY,CAAC;;;;;;uCAMP,SAAS,CAAC,qBAAqB,CAAC;kBACrD,SAAS,CAAC,mBAAmB,CAAC;;;;;;;;;;;;;kCAad,SAAS;;;;;;;;;;;;;;0DAce,SAAS,CAAC,uBAAuB,CAAC;0CAClD,SAAS,CAAC,uBAAuB,CAAC;+BAC7C,GAAG,IAAI,GAAG;;6BAEZ,SAAS,CAAC,YAAY,CAAC,MAAM,GAAG;wBACrC,SAAS,CAAC,YAAY,CAAC;;;;;;;;;uBASxB,SAAS,CAAC,YAAY,CAAC;4BAClB,SAAS,CAAC,YAAY,CAAC,MAAM,GAAG;;;;;;;;;;4BAUhC,SAAS,CAAC,iBAAiB,CAAC;;;;;;iCAMvB,SAAS,CAAC,kBAAkB,CAAC;;;;;;;;;;;;4BAYlC,SAAS,CAAC,mBAAmB,CAAC;;KAErD,CAAC;IAEO,MAAM;QACX,OAAO,IAAI,CAAA,eAAe,CAAC;IAC/B,CAAC;;AAGL,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,oEAAoE;AACpE,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rak200/ui",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Host-agnostic custom-element UI components for the rak200 ecosystem",
5
5
  "license": "MIT",
6
6
  "author": "rak200 <rak.ricardo@windowslive.com>",