@noctuatech/uswds 0.0.32 → 0.0.34

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.
Files changed (72) hide show
  1. package/README.md +13 -1
  2. package/assets/img/gog-logo.png +0 -0
  3. package/assets/img/wosb1.jpg +0 -0
  4. package/package.json +12 -27
  5. package/src/lib/accordion/accordion.element.ts +1 -0
  6. package/src/lib/alert/alert.element.ts +0 -7
  7. package/src/lib/checkbox/checkbox.element.ts +23 -8
  8. package/src/lib/checkbox/checkbox.stories.ts +2 -0
  9. package/src/lib/collection/collection-item/collection-item.element.ts +54 -0
  10. package/src/lib/collection/collection-item/collection-item.test.ts +15 -0
  11. package/src/lib/collection/collection.element.ts +24 -0
  12. package/src/lib/collection/collection.stories.ts +70 -0
  13. package/src/lib/collection/collection.test.ts +15 -0
  14. package/src/lib/define.ts +2 -0
  15. package/src/lib/icon/icon-types.ts +0 -8
  16. package/src/lib/input/input.element.ts +10 -17
  17. package/src/lib/input/input.stories.ts +1 -1
  18. package/src/lib/input-mask/format.ts +24 -22
  19. package/src/lib/input-mask/input-mask.element.ts +4 -4
  20. package/src/lib/radio/radio.element.ts +9 -5
  21. package/src/lib/select/select-option/select-option.element.ts +4 -2
  22. package/src/lib/textarea/textarea.element.ts +13 -16
  23. package/src/lib.ts +2 -0
  24. package/target/lib/accordion/accordion.element.js.map +1 -1
  25. package/target/lib/alert/alert.element.js +0 -7
  26. package/target/lib/alert/alert.element.js.map +1 -1
  27. package/target/lib/checkbox/checkbox.element.d.ts +1 -0
  28. package/target/lib/checkbox/checkbox.element.js +29 -9
  29. package/target/lib/checkbox/checkbox.element.js.map +1 -1
  30. package/target/lib/checkbox/checkbox.stories.d.ts +1 -0
  31. package/target/lib/checkbox/checkbox.stories.js +2 -0
  32. package/target/lib/checkbox/checkbox.stories.js.map +1 -1
  33. package/target/lib/collection/collection-item/collection-item.element.d.ts +7 -0
  34. package/target/lib/collection/collection-item/collection-item.element.js +66 -0
  35. package/target/lib/collection/collection-item/collection-item.element.js.map +1 -0
  36. package/target/lib/collection/collection-item/collection-item.test.d.ts +1 -0
  37. package/target/lib/collection/collection-item/collection-item.test.js +11 -0
  38. package/target/lib/collection/collection-item/collection-item.test.js.map +1 -0
  39. package/target/lib/collection/collection.element.d.ts +7 -0
  40. package/target/lib/collection/collection.element.js +36 -0
  41. package/target/lib/collection/collection.element.js.map +1 -0
  42. package/target/lib/collection/collection.stories.d.ts +12 -0
  43. package/target/lib/collection/collection.stories.js +63 -0
  44. package/target/lib/collection/collection.stories.js.map +1 -0
  45. package/target/lib/collection/collection.test.d.ts +1 -0
  46. package/target/lib/collection/collection.test.js +11 -0
  47. package/target/lib/collection/collection.test.js.map +1 -0
  48. package/target/lib/define.d.ts +2 -0
  49. package/target/lib/define.js +2 -0
  50. package/target/lib/define.js.map +1 -1
  51. package/target/lib/icon/icon-types.d.ts +1 -1
  52. package/target/lib/icon/icon-types.js +0 -8
  53. package/target/lib/icon/icon-types.js.map +1 -1
  54. package/target/lib/input/input.element.d.ts +2 -2
  55. package/target/lib/input/input.element.js +14 -16
  56. package/target/lib/input/input.element.js.map +1 -1
  57. package/target/lib/input/input.stories.js +1 -1
  58. package/target/lib/input-mask/format.d.ts +3 -6
  59. package/target/lib/input-mask/format.js +21 -22
  60. package/target/lib/input-mask/format.js.map +1 -1
  61. package/target/lib/input-mask/input-mask.element.js +3 -3
  62. package/target/lib/input-mask/input-mask.element.js.map +1 -1
  63. package/target/lib/radio/radio.element.js +9 -5
  64. package/target/lib/radio/radio.element.js.map +1 -1
  65. package/target/lib/select/select-option/select-option.element.js +4 -2
  66. package/target/lib/select/select-option/select-option.element.js.map +1 -1
  67. package/target/lib/textarea/textarea.element.js +12 -16
  68. package/target/lib/textarea/textarea.element.js.map +1 -1
  69. package/target/lib.d.ts +2 -0
  70. package/target/lib.js +2 -0
  71. package/target/lib.js.map +1 -1
  72. package/assets/uswds.min.js +0 -1
package/README.md CHANGED
@@ -23,7 +23,7 @@ npm i @noctuatech/uswds
23
23
  }
24
24
  </style>
25
25
 
26
- <script src="/node_modules/@noctuatech/uswds/assets/uswds.min.js"></script>
26
+ <script type="module" src="https://esm.sh/@noctuatech/uswds@latest"></script>
27
27
 
28
28
  <usa-config icon-path="/node_modules/@noctuatech/uswds/assets/usa-icons/">
29
29
  <usa-alert type="info">
@@ -33,6 +33,18 @@ npm i @noctuatech/uswds
33
33
  <usa-link href="#">consectetur adipiscing</usa-link>&nbsp;elit, sed do
34
34
  eiusmod.
35
35
  </usa-alert>
36
+
37
+ <form>
38
+ <usa-input name="username">
39
+ First name
40
+ </usa-input>
41
+
42
+ <usa-input name="password">
43
+ Last name
44
+ </usa-input>
45
+
46
+ <usa-button type="submit">Submit</usa-button>
47
+ </form>
36
48
  </usa-config>
37
49
  ```
38
50
 
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noctuatech/uswds",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "type": "module",
5
5
  "workspaces": ["packages/**"],
6
6
  "main": "./target/lib.js",
@@ -29,7 +29,7 @@
29
29
  "dependencies": ["tsc"]
30
30
  },
31
31
  "build": {
32
- "dependencies": ["tsc", "build-storybook", "rollup", "copy_icons"]
32
+ "dependencies": ["tsc", "build-storybook", "copy_icons"]
33
33
  },
34
34
  "preview": {
35
35
  "command": "browser-sync start --server --files \"target/**\" \"index.html\"",
@@ -51,17 +51,6 @@
51
51
  "output": ["target/**", "tsconfig.tsbuildinfo"],
52
52
  "dependencies": ["./packages/testing:build"]
53
53
  },
54
- "rollup": {
55
- "command": "rollup -c rollup.config.js",
56
- "files": ["target/**", "rollup.config.js"],
57
- "output": ["assets/uswds.min.js"],
58
- "dependencies": [
59
- {
60
- "script": "tsc",
61
- "cascade": false
62
- }
63
- ]
64
- },
65
54
  "copy_icons": {
66
55
  "command": "cp -a node_modules/@uswds/uswds/dist/img/usa-icons/. assets/usa-icons/",
67
56
  "files": ["node_modules/@uswds/uswds/dist/img/usa-icons/**"],
@@ -72,9 +61,9 @@
72
61
  "license": "ISC",
73
62
  "description": "",
74
63
  "dependencies": {
75
- "@joist/di": "^4.0.0-next.51",
76
- "@joist/element": "^4.0.0-next.51",
77
- "@joist/observable": "^4.0.0-next.51",
64
+ "@joist/di": "^4.0.0",
65
+ "@joist/element": "^4.0.0",
66
+ "@joist/observable": "^4.0.0",
78
67
  "tslib": "2.8.1"
79
68
  },
80
69
  "devDependencies": {
@@ -82,29 +71,25 @@
82
71
  "@open-wc/testing": "^4.0.0",
83
72
  "@rollup/plugin-node-resolve": "^16.0.0",
84
73
  "@rollup/plugin-terser": "^0.4.4",
85
- "@storybook/addon-essentials": "^8.5.1",
86
- "@storybook/web-components": "^8.5.1",
87
- "@storybook/web-components-vite": "^8.5.1",
74
+ "@storybook/addon-essentials": "^8.6.0",
75
+ "@storybook/web-components": "^8.6.0",
76
+ "@storybook/web-components-vite": "^8.6.0",
88
77
  "@types/mocha": "^10.0.7",
89
78
  "@types/node": "^22.0.0",
90
79
  "@uswds/uswds": "^3.10.0",
91
- "@web/test-runner": "^0.19.0",
80
+ "@web/test-runner": "^0.20.0",
92
81
  "browser-sync": "^3.0.3",
93
82
  "husky": "^9.0.11",
94
83
  "js-beautify": "^1.15.1",
95
84
  "lint-staged": "^15.2.2",
96
85
  "lit": "^3.2.1",
97
- "minify-html-literals": "^1.3.5",
98
86
  "mocha": "^11.0.0",
99
87
  "plop": "^4.0.1",
100
- "rollup": "^4.28.0",
101
- "storybook": "^8.5.1",
102
- "typescript": "^5.6.3",
88
+ "storybook": "^8.6.0",
89
+ "typescript": "^5.8.0-beta",
103
90
  "wireit": "^0.14.9"
104
91
  },
105
92
  "lint-staged": {
106
- "*": [
107
- "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
108
- ]
93
+ "*": ["biome check --write --no-errors-on-unmatched"]
109
94
  }
110
95
  }
@@ -1,4 +1,5 @@
1
1
  import { attr, css, element, html, listen, query } from "@joist/element";
2
+
2
3
  import { USAAccordionToggleEvent } from "./events.js";
3
4
 
4
5
  declare global {
@@ -65,13 +65,6 @@ declare global {
65
65
  }
66
66
 
67
67
  #heading::slotted(*) {
68
- font-family:
69
- Source Sans Pro Web,
70
- Helvetica Neue,
71
- Helvetica,
72
- Roboto,
73
- Arial,
74
- sans-serif;
75
68
  font-size: 1.33rem;
76
69
  line-height: 0.9;
77
70
  margin-top: 0;
@@ -54,6 +54,16 @@ declare global {
54
54
  box-shadow: 0 0 0 2px #005ea2;
55
55
  }
56
56
 
57
+ :host([disabled]) .checkbox {
58
+ background-color: #fff;
59
+ box-shadow: 0 0 0 2px #757575;
60
+ }
61
+
62
+ :host([disabled]) label {
63
+ color: #757575;
64
+ cursor: not-allowed;
65
+ }
66
+
57
67
  input:checked + .checkbox::after {
58
68
  content: " ";
59
69
  display: block;
@@ -84,12 +94,6 @@ declare global {
84
94
  height: 0;
85
95
  }
86
96
 
87
- .spacer {
88
- height: 1.25rem;
89
- width: 1.25rem;
90
- margin-right: 0.75rem;
91
- }
92
-
93
97
  :host([tiled]) label:has(input:checked) {
94
98
  background-color: rgba(0, 94, 162, 0.1);
95
99
  border-color: #005ea2;
@@ -123,6 +127,9 @@ export class USACheckboxElement extends HTMLElement {
123
127
  @attr()
124
128
  accessor required = false;
125
129
 
130
+ @attr()
131
+ accessor disabled = false;
132
+
126
133
  @attr({
127
134
  observed: false,
128
135
  })
@@ -133,13 +140,21 @@ export class USACheckboxElement extends HTMLElement {
133
140
  #internals = this.attachInternals();
134
141
 
135
142
  connectedCallback() {
136
- this.#checkbox({ checked: this.checked, name: this.name });
143
+ this.#checkbox({
144
+ checked: this.checked,
145
+ name: this.name,
146
+ disabled: this.disabled,
147
+ });
137
148
 
138
149
  this.#syncFormState();
139
150
  }
140
151
 
141
152
  attributeChangedCallback() {
142
- this.#checkbox({ checked: this.checked, name: this.name });
153
+ this.#checkbox({
154
+ checked: this.checked,
155
+ name: this.name,
156
+ disabled: this.disabled,
157
+ });
143
158
 
144
159
  this.#syncFormState();
145
160
  }
@@ -17,6 +17,7 @@ const meta = {
17
17
  value=${ifDefined(args.value)}
18
18
  checked=${ifDefined(args.checked)}
19
19
  ?tiled=${args.tiled}
20
+ ?disabled=${args.disabled}
20
21
  >
21
22
  Hello World
22
23
  ${when(
@@ -44,6 +45,7 @@ const meta = {
44
45
  name: "toc",
45
46
  value: "agree",
46
47
  tiled: false,
48
+ disabled: false,
47
49
  },
48
50
  } satisfies Meta<USACheckboxElement & { description: string }>;
49
51
 
@@ -0,0 +1,54 @@
1
+ import { css, element, html } from "@joist/element";
2
+
3
+ declare global {
4
+ interface HTMLElementTagNameMap {
5
+ "usa-collection-item": USACollectionItemElement;
6
+ }
7
+ }
8
+
9
+ @element({
10
+ tagName: "usa-collection-item",
11
+ shadowDom: [
12
+ css`
13
+ :host {
14
+ display: flex;
15
+ gap: 1rem;
16
+ }
17
+
18
+ .content {
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+
23
+ slot[name="heading"]::slotted(*) {
24
+ font-size: 1.13rem;
25
+ line-height: 1.3;
26
+ margin-bottom: 0;
27
+ margin-top: 0;
28
+ }
29
+
30
+ .description {
31
+ margin-bottom: .5rem;
32
+ margin-top: .5rem;
33
+ }
34
+ `,
35
+ html`
36
+ <slot name="img"></slot>
37
+
38
+ <div class="content">
39
+ <div class="heading">
40
+ <slot name="heading"></slot>
41
+ </div>
42
+
43
+ <div class="description">
44
+ <slot name="description"></slot>
45
+ </div>
46
+
47
+ <div class="meta">
48
+ <slot name="meta"></slot>
49
+ </div>
50
+ </div>
51
+ `,
52
+ ],
53
+ })
54
+ export class USACollectionItemElement extends HTMLElement {}
@@ -0,0 +1,15 @@
1
+ import "./collection-item.element.js";
2
+
3
+ import { assert, fixture, html } from "@open-wc/testing";
4
+
5
+ import type { USACollectionItemElement } from "./collection-item.element.js";
6
+
7
+ describe("usa-collection-item", () => {
8
+ it("should be accessible", async () => {
9
+ const collectionItem = await fixture<USACollectionItemElement>(html`
10
+ <usa-collection-item>Hello World</usa-collection-item>
11
+ `);
12
+
13
+ return assert.isAccessible(collectionItem);
14
+ });
15
+ });
@@ -0,0 +1,24 @@
1
+ import { css, element, html } from "@joist/element";
2
+
3
+ declare global {
4
+ interface HTMLElementTagNameMap {
5
+ "usa-collection": USACollectionElement;
6
+ }
7
+ }
8
+
9
+ @element({
10
+ tagName: "usa-collection",
11
+ shadowDom: [
12
+ css`
13
+ :host {
14
+ display: grid;
15
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
16
+ gap: 2rem;
17
+ }
18
+ `,
19
+ html`
20
+ <slot></slot>
21
+ `,
22
+ ],
23
+ })
24
+ export class USACollectionElement extends HTMLElement {}
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryObj } from "@storybook/web-components";
2
+ import { html } from "lit";
3
+
4
+ import type { USACollectionElement } from "./collection.element.js";
5
+
6
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
7
+ const meta = {
8
+ title: "usa-collection",
9
+ tags: ["autodocs"],
10
+ render(args) {
11
+ return html`
12
+ <usa-collection>
13
+ <usa-collection-item>
14
+ <h4 slot="heading">
15
+ <usa-link href="#">Gears of Government President's Award winners</usa-link>
16
+ </h4>
17
+
18
+ <div slot="description">
19
+ Today, the Administration announces the winners of the Gears of Government President’s Award. This program recognizes the contributions of individuals and teams across the federal workforce who make a profound difference in the lives of the American people.
20
+ </div>
21
+ </usa-collection-item>
22
+
23
+ <usa-collection-item>
24
+ <img height="80" width="80" src="/assets/img/gog-logo.png" slot="img"/>
25
+
26
+ <h4 slot="heading">
27
+ <usa-link href="#">Gears of Government President's Award winners</usa-link>
28
+ </h4>
29
+
30
+ <div slot="description">
31
+ Today, the Administration announces the winners of the Gears of Government President’s Award. This program recognizes the contributions of individuals and teams across the federal workforce who make a profound difference in the lives of the American people.
32
+ </div>
33
+ </usa-collection-item>
34
+
35
+ <usa-collection-item>
36
+ <h4 slot="heading">
37
+ <usa-link href="#">Women-owned small business dashboard</usa-link>
38
+ </h4>
39
+
40
+ <div slot="description">
41
+ In honor of National Women's Small Business Month, we've partnered with SBA's Office of Government Contracting and Business Development and Office of Program Performance, Analysis, and Evaluation to highlight the Women-Owned Small Businesses (WOSBs) data dashboard!
42
+ </div>
43
+ </usa-collection-item>
44
+
45
+ <usa-collection-item>
46
+ <img height="52" width="80" src="/assets/img/wosb1.jpg" slot="img"/>
47
+
48
+ <h4 slot="heading">
49
+ <usa-link href="#">Women-owned small business dashboard</usa-link>
50
+ </h4>
51
+
52
+ <div slot="description">
53
+ In honor of National Women's Small Business Month, we've partnered with SBA's Office of Government Contracting and Business Development and Office of Program Performance, Analysis, and Evaluation to highlight the Women-Owned Small Businesses (WOSBs) data dashboard!
54
+ </div>
55
+ </usa-collection-item>
56
+ </usa-collection>
57
+ `;
58
+ },
59
+ argTypes: {},
60
+ args: {},
61
+ } satisfies Meta<USACollectionElement>;
62
+
63
+ export default meta;
64
+
65
+ type Story = StoryObj<USACollectionElement>;
66
+
67
+ // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
68
+ export const Primary: Story = {
69
+ args: {},
70
+ };
@@ -0,0 +1,15 @@
1
+ import "./collection.element.js";
2
+
3
+ import { assert, fixture, html } from "@open-wc/testing";
4
+
5
+ import type { USACollectionElement } from "./collection.element.js";
6
+
7
+ describe("usa-collection", () => {
8
+ it("should be accessible", async () => {
9
+ const collection = await fixture<USACollectionElement>(html`
10
+ <usa-collection>Hello World</usa-collection>
11
+ `);
12
+
13
+ return assert.isAccessible(collection);
14
+ });
15
+ });
package/src/lib/define.ts CHANGED
@@ -30,3 +30,5 @@ import "./card/card-footer/card-footer.element.js";
30
30
  import "./card/card-media/card-media.element.js";
31
31
  import "./card/card-group/card-group.element.js";
32
32
  import "./textarea/textarea.element.js";
33
+ import "./collection/collection.element.js";
34
+ import "./collection/collection-item/collection-item.element.js";
@@ -71,7 +71,6 @@ export const ICON_TYPES = [
71
71
  "event",
72
72
  "expand_less",
73
73
  "expand_more",
74
- "facebook",
75
74
  "fast_forward",
76
75
  "fast_rewind",
77
76
  "favorite",
@@ -85,7 +84,6 @@ export const ICON_TYPES = [
85
84
  "fingerprint",
86
85
  "first_page",
87
86
  "flag",
88
- "flickr",
89
87
  "flight",
90
88
  "flooding",
91
89
  "folder",
@@ -93,7 +91,6 @@ export const ICON_TYPES = [
93
91
  "format_quote",
94
92
  "format_size",
95
93
  "forum",
96
- "github",
97
94
  "grid_view",
98
95
  "group_add",
99
96
  "groups",
@@ -112,7 +109,6 @@ export const ICON_TYPES = [
112
109
  "info",
113
110
  "info_outline",
114
111
  "insights",
115
- "instagram",
116
112
  "keyboard",
117
113
  "label",
118
114
  "language",
@@ -122,7 +118,6 @@ export const ICON_TYPES = [
122
118
  "lightbulb_outline",
123
119
  "link",
124
120
  "link_off",
125
- "linkedin",
126
121
  "list",
127
122
  "local_cafe",
128
123
  "local_fire_department",
@@ -222,7 +217,6 @@ export const ICON_TYPES = [
222
217
  "translate",
223
218
  "trending_down",
224
219
  "trending_up",
225
- "twitter",
226
220
  "undo",
227
221
  "unfold_less",
228
222
  "unfold_more",
@@ -237,8 +231,6 @@ export const ICON_TYPES = [
237
231
  "wash",
238
232
  "wifi",
239
233
  "work",
240
- "x",
241
- "youtube",
242
234
  "zoom_in",
243
235
  "zoom_out_map",
244
236
  "zoom_out",
@@ -1,5 +1,5 @@
1
1
  import { attr, css, element, html, listen, query, ready } from "@joist/element";
2
- import { type Changes, effect, observe } from "@joist/observable";
2
+ import { effect, observe } from "@joist/observable";
3
3
 
4
4
  import type { MaskableElement } from "../input-mask/maskable.element.js";
5
5
 
@@ -105,6 +105,9 @@ export class USATextInputElement
105
105
  @attr()
106
106
  accessor required = false;
107
107
 
108
+ @attr()
109
+ accessor type: "text" | "password" | "number" = "text";
110
+
108
111
  @attr({
109
112
  observed: false,
110
113
  })
@@ -131,11 +134,9 @@ export class USATextInputElement
131
134
  }
132
135
 
133
136
  attributeChangedCallback() {
134
- this.#input({
135
- autocomplete: this.autocomplete,
136
- placeholder: this.placeholder,
137
- name: this.name,
138
- });
137
+ const { autocomplete, placeholder, name, type } = this;
138
+
139
+ this.#input({ autocomplete, placeholder, name, type });
139
140
  }
140
141
 
141
142
  connectedCallback() {
@@ -143,18 +144,10 @@ export class USATextInputElement
143
144
  }
144
145
 
145
146
  @effect()
146
- onChange(changes: Changes<this>) {
147
- const input = this.#input({
148
- value: this.value,
149
- });
147
+ onChange() {
148
+ const { value, selectionStart, selectionEnd } = this;
150
149
 
151
- if (changes.has("selectionStart")) {
152
- input.selectionStart = this.selectionStart;
153
- }
154
-
155
- if (changes.has("selectionEnd")) {
156
- input.selectionEnd = this.selectionEnd;
157
- }
150
+ this.#input({ value, selectionStart, selectionEnd });
158
151
 
159
152
  this.#syncFormState();
160
153
  }
@@ -16,7 +16,7 @@ const meta = {
16
16
 
17
17
  console.log(Array.from(data.entries()));
18
18
  }}>
19
- <usa-input name="fname" value="Danny" autocomplete="off" detail="pfx">
19
+ <usa-input name="fname" autocomplete="off" detail="pfx">
20
20
  <usa-icon icon="credit_card" slot="detail"></usa-icon>
21
21
 
22
22
  First name
@@ -1,10 +1,6 @@
1
- export enum PatternChar {
2
- Any = "*",
3
- Number = "9",
4
- Letter = "A",
5
- }
1
+ export const PATTERN_CHARS = ["*", "9", "A"] as const;
6
2
 
7
- export const PATTERN_CHARS = Object.values(PatternChar);
3
+ export type PatternChar = (typeof PATTERN_CHARS)[number];
8
4
 
9
5
  export const REG_EXPS = {
10
6
  Letters: /^[a-z]/i,
@@ -28,24 +24,26 @@ export function format(value: string, pattern: string): FormattedResult {
28
24
  const char = chars[count];
29
25
 
30
26
  if (char && patternChar) {
31
- if (patternChar === PatternChar.Any) {
32
- // Any letter or number
33
- formatted += char;
34
- count++;
35
- } else if (patternChar === PatternChar.Number) {
36
- // Numbers only
37
- if (/^[0-9]/i.test(char)) {
38
- formatted += char;
39
- }
40
-
41
- count++;
42
- } else if (patternChar === PatternChar.Letter) {
43
- // Letters only
44
- if (/^[a-z]/i.test(char)) {
27
+ if (isPatternChar(patternChar)) {
28
+ if (patternChar === "*") {
29
+ // Any letter or number
45
30
  formatted += char;
31
+ count++;
32
+ } else if (patternChar === "9") {
33
+ // Numbers only
34
+ if (/^[0-9]/i.test(char)) {
35
+ formatted += char;
36
+ }
37
+
38
+ count++;
39
+ } else if (patternChar === "A") {
40
+ // Letters only
41
+ if (/^[a-z]/i.test(char)) {
42
+ formatted += char;
43
+ }
44
+
45
+ count++;
46
46
  }
47
-
48
- count++;
49
47
  } else {
50
48
  formatted += patternChar;
51
49
  }
@@ -54,3 +52,7 @@ export function format(value: string, pattern: string): FormattedResult {
54
52
 
55
53
  return { raw, formatted };
56
54
  }
55
+
56
+ export function isPatternChar(char: unknown): char is PatternChar {
57
+ return PATTERN_CHARS.some((c) => char === c);
58
+ }
@@ -1,6 +1,6 @@
1
1
  import { attr, css, element, html, listen, queryAll } from "@joist/element";
2
2
 
3
- import { PATTERN_CHARS, PatternChar, REG_EXPS, format } from "./format.js";
3
+ import { PATTERN_CHARS, type PatternChar, REG_EXPS, format } from "./format.js";
4
4
  import type { MaskableElement } from "./maskable.element.js";
5
5
 
6
6
  declare global {
@@ -64,7 +64,7 @@ export class USAInputMaskElement extends HTMLElement {
64
64
  onKeyDown(e: KeyboardEvent) {
65
65
  const input = e.target as MaskableElement;
66
66
  const mask = this.#getMaskFor(input);
67
- const patternChar = mask[input.selectionStart || 0];
67
+ const patternChar = mask[input.selectionStart || 0] as PatternChar;
68
68
 
69
69
  if (e.key.length === 1 && /^[a-z0-9]/i.test(e.key)) {
70
70
  // check that the key is a single character and that it is a letter or number
@@ -72,12 +72,12 @@ export class USAInputMaskElement extends HTMLElement {
72
72
  if (input.value.length >= mask.length) {
73
73
  // prevent default once value is the same as the mask length
74
74
  e.preventDefault();
75
- } else if (patternChar === PatternChar.Number) {
75
+ } else if (patternChar === "9") {
76
76
  if (!REG_EXPS.Numbers.test(e.key)) {
77
77
  // if pattern char specifies number and is not
78
78
  e.preventDefault();
79
79
  }
80
- } else if (patternChar === PatternChar.Letter) {
80
+ } else if (patternChar === "A") {
81
81
  if (!REG_EXPS.Letters.test(e.key)) {
82
82
  // if pattern char specifies letter and is not
83
83
  e.preventDefault();
@@ -18,6 +18,10 @@ declare global {
18
18
  shadowDom: [
19
19
  css`
20
20
  :host {
21
+ --usa-radio-active-color: #005ea2;
22
+ --usa-radio-tiled-bg: rgba(0, 94, 162, 0.1);
23
+ --usa-radio-focus-color: #2491ff;
24
+
21
25
  display: flex;
22
26
  flex-direction: column;
23
27
  gap: 1rem;
@@ -51,14 +55,14 @@ declare global {
51
55
  }
52
56
 
53
57
  label:has(input:checked)::before {
54
- background-color: #005ea2;
58
+ background-color: var(--usa-radio-active-color);
55
59
  box-shadow:
56
- 0 0 0 2px #005ea2,
60
+ 0 0 0 2px var(--usa-radio-active-color),
57
61
  inset 0 0 0 2px #fff;
58
62
  }
59
63
 
60
64
  label:has(input:focus)::before {
61
- outline: 0.25rem solid #2491ff;
65
+ outline: 0.25rem solid var(--usa-radio-focus-color);
62
66
  outline-offset: 0.25rem;
63
67
  }
64
68
 
@@ -75,8 +79,8 @@ declare global {
75
79
  }
76
80
 
77
81
  :host([tiled]) label:has(input:checked) {
78
- background-color: rgba(0, 94, 162, 0.1);
79
- border-color: #005ea2;
82
+ background-color: var(--usa-radio-tiled-bg);
83
+ border-color: var(--usa-radio-active-color);
80
84
  }
81
85
 
82
86
  slot {