@api-client/ui 0.3.0 → 0.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -57,7 +57,8 @@
57
57
  "./types/*": "./build/src/types/*"
58
58
  },
59
59
  "scripts": {
60
- "build": "npm run tsc && npm run lint",
60
+ "build": "wireit",
61
+ "copy-assets": "wireit",
61
62
  "lint": "npm run lint:prettier && npm run lint:eslint",
62
63
  "lint:eslint": "wireit",
63
64
  "lint:prettier": "wireit",
@@ -73,6 +74,17 @@
73
74
  "tsc:all": "wireit"
74
75
  },
75
76
  "wireit": {
77
+ "build": {
78
+ "command": "npm run copy-assets",
79
+ "dependencies": [
80
+ "tsc",
81
+ "lint:prettier",
82
+ "lint:eslint"
83
+ ]
84
+ },
85
+ "copy-assets": {
86
+ "command": "node scripts/copy-assets.js"
87
+ },
76
88
  "lint:eslint": {
77
89
  "command": "eslint --color --cache --cache-location .eslintcache .",
78
90
  "files": [
@@ -97,11 +109,15 @@
97
109
  },
98
110
  "test": {
99
111
  "command": "wtr --playwright --browsers chromium",
100
- "dependencies": ["tsc:all"]
112
+ "dependencies": [
113
+ "tsc:all"
114
+ ]
101
115
  },
102
116
  "test:coverage": {
103
117
  "command": "wtr --playwright --browsers chromium --coverage",
104
- "dependencies": ["tsc:all"]
118
+ "dependencies": [
119
+ "tsc:all"
120
+ ]
105
121
  },
106
122
  "tsc:all": {
107
123
  "command": "tsc --project tsconfig.all.json",
@@ -0,0 +1,21 @@
1
+ import { copyFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
4
+ async function copyStyles() {
5
+ const src = join('src', 'styles', 'm3');
6
+ const dest = join('build', 'src', 'styles', 'm3');
7
+ const files = [
8
+ 'native.css',
9
+ 'theme.css',
10
+ 'tokens.css',
11
+ ];
12
+ await mkdir(dest, { recursive: true });
13
+ await Promise.all(
14
+ files.map(async (name) => {
15
+ const destFile = join(dest, name);
16
+ await copyFile(join(src, name), destFile);
17
+ })
18
+ );
19
+ }
20
+
21
+ await copyStyles();
@@ -213,7 +213,7 @@ export default abstract class Input extends UiElement {
213
213
  *
214
214
  * Possible values are:
215
215
  *
216
- * - `off` or `none`: No autocapitalization is applied (all letters default to lowercase)
216
+ * - `off` or `none`: No auto-capitalization is applied (all letters default to lowercase)
217
217
  * - `on` or `sentences`: The first letter of each sentence defaults to a capital letter;
218
218
  * all other letters default to lowercase
219
219
  * - `words`: The first letter of each word defaults to a capital letter; all other letters default to lowercase
@@ -586,7 +586,7 @@ export default abstract class Input extends UiElement {
586
586
  }
587
587
 
588
588
  /**
589
- * Unfocuses the text field.
589
+ * Un-focuses the text field.
590
590
  */
591
591
  override blur(): void {
592
592
  this.input?.blur()
@@ -638,7 +638,7 @@ export default abstract class Input extends UiElement {
638
638
  }
639
639
 
640
640
  protected getErrorText(): string {
641
- return this.invalid ? this.invalidText : this.nativeErrorText
641
+ return this.invalidText || this.nativeErrorText
642
642
  }
643
643
 
644
644
  /**
@@ -750,14 +750,16 @@ export default abstract class Input extends UiElement {
750
750
  return
751
751
  }
752
752
  this.focused = true
753
- this.#_userInteracted = true
754
753
  this.removingTabindex = true
755
754
  // Note, the input is automatically focused when form is being submitted and
756
755
  // it won't pass validation. In this case the form will throw an error
757
756
  // as the input is not focusable. In overall, this still works with forms and validation.
758
- this.dataset.tabindex = this.getAttribute('tabindex') || '0'
759
- this.removeAttribute('tabindex')
757
+ if (this.checkNativeValidity()) {
758
+ this.dataset.tabindex = this.getAttribute('tabindex') || '0'
759
+ this.removeAttribute('tabindex')
760
+ }
760
761
  input.focus()
762
+ this.#_userInteracted = true
761
763
  setTimeout(() => {
762
764
  this.removingTabindex = false
763
765
  }, 1)
@@ -774,9 +776,12 @@ export default abstract class Input extends UiElement {
774
776
  this.setAttribute('tabindex', index)
775
777
  }
776
778
 
779
+ private checkNativeValidity(): boolean {
780
+ return this.input?.validity.valid ?? true
781
+ }
782
+
777
783
  private checkValidityAndDispatch(): { valid: boolean; canceled: boolean } {
778
- const { input } = this
779
- const valid = input?.checkValidity() ?? true
784
+ const valid = this.input?.checkValidity() ?? true
780
785
  let canceled = false
781
786
  if (!valid) {
782
787
  canceled = !this.dispatchEvent(new Event('invalid', { cancelable: true }))
@@ -853,10 +858,10 @@ export default abstract class Input extends UiElement {
853
858
 
854
859
  protected getAriaDescribedBy(): string {
855
860
  const hasSupport = !!this.getSupportingText()
856
- const hasCounter = this.hasCounter()
857
861
  if (hasSupport) {
858
862
  return this.supportingTextId
859
863
  }
864
+ const hasCounter = this.hasCounter()
860
865
  if (hasCounter) {
861
866
  return this.counterId
862
867
  }
@@ -941,10 +946,10 @@ export default abstract class Input extends UiElement {
941
946
  return html` <span class="supporting-text"> ${this.renderSupportingTextValue()} ${this.renderCounter()} </span> `
942
947
  }
943
948
 
944
- protected renderSupportingTextValue(): TemplateResult {
949
+ protected renderSupportingTextValue(): TemplateResult | typeof nothing {
945
950
  const text = this.getSupportingText()
946
951
  if (!text) {
947
- return html``
952
+ return nothing
948
953
  }
949
954
  const shouldAlert = this.shouldErrorAnnounce()
950
955
  return html`
@@ -135,24 +135,26 @@ export default css`
135
135
  .supporting-text {
136
136
  padding-top: 4px;
137
137
  color: var(--md-sys-color-on-surface-variant);
138
- font-family: var(--md-sys-typescale-body-medium-font);
139
- font-weight: var(--md-sys-typescale-body-medium-weight);
140
- font-size: var(--md-sys-typescale-body-medium-size);
141
- letter-spacing: var(--md-sys-typescale-body-medium-tracking);
142
- line-height: var(--md-sys-typescale-body-medium-height);
138
+ font-family: var(--md-sys-typescale-body-small-font);
139
+ font-weight: var(--md-sys-typescale-body-small-weight);
140
+ font-size: var(--md-sys-typescale-body-small-size);
141
+ letter-spacing: var(--md-sys-typescale-body-small-tracking);
142
+ line-height: var(--md-sys-typescale-body-small-height);
143
143
  display: flex;
144
144
  justify-content: space-between;
145
145
  padding: 0 16px;
146
146
  }
147
147
 
148
148
  .supporting-text-start {
149
- overflow: hidden;
150
- text-overflow: ellipsis;
151
- white-space: nowrap;
152
149
  flex: 1;
153
150
  margin-right: 16px;
154
151
  }
155
152
 
153
+ .supporting-text-end {
154
+ margin-left: auto;
155
+ flex-shrink: 0;
156
+ }
157
+
156
158
  :host([disabled]) {
157
159
  pointer-events: none;
158
160
  }
@@ -171,6 +173,7 @@ export default css`
171
173
  }
172
174
 
173
175
  :host([invalid]) .label,
176
+ :host([invalid]:focus-within) .label,
174
177
  :host([invalid]) .supporting-text,
175
178
  :host([invalid]) .end ::slotted(*) {
176
179
  color: var(--md-sys-color-error);
@@ -87,6 +87,10 @@ export default css`
87
87
  border-bottom-color: var(--md-sys-color-on-error-container);
88
88
  }
89
89
 
90
+ :host([invalid]) .highlight::after {
91
+ border-bottom-color: var(--md-sys-color-error);
92
+ }
93
+
90
94
  .labelHidden .body {
91
95
  padding-top: 0;
92
96
  }
@@ -1,6 +1,12 @@
1
1
  import { css } from 'lit'
2
2
 
3
3
  export default css`
4
+ :host {
5
+ --_border-color: var(--md-sys-color-outline);
6
+ --_outline-color: var(--md-sys-color-primary);
7
+ --_outline-width: 0px;
8
+ }
9
+
4
10
  .highlight {
5
11
  display: none;
6
12
  }
@@ -10,21 +16,35 @@ export default css`
10
16
  }
11
17
 
12
18
  .container {
13
- border: 1px var(--md-sys-color-outline) solid;
14
- outline: 0px solid var(--md-sys-color-primary);
15
- outline-offset: -3px;
19
+ border: 1px var(--_border-color) solid;
20
+ outline: var(--_outline-width) solid var(--_outline-color);
21
+ outline-offset: calc(-1 * var(--_outline-width));
16
22
  transition:
17
23
  border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
18
- outline-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
24
+ outline-width 150ms cubic-bezier(0.4, 0, 0.2, 1),
25
+ outline-offset 150ms cubic-bezier(0.4, 0, 0.2, 1);
26
+ }
27
+
28
+ :host(:hover:not(:focus-within):not([invalid])) {
29
+ --_border-color: var(--md-sys-color-on-surface);
30
+ }
31
+
32
+ :host(:focus-within:not([invalid])) {
33
+ --_border-color: var(--md-sys-color-primary);
34
+ }
35
+
36
+ :host(:focus-within) {
37
+ --_outline-width: 3px;
19
38
  }
20
39
 
21
- :host(:hover:not(:focus-within)) .container {
22
- border-color: var(--md-sys-color-on-surface);
40
+ :host([invalid]) {
41
+ --_border-color: var(--md-sys-color-error);
42
+ --_outline-color: var(--md-sys-color-error);
23
43
  }
24
44
 
25
- :host(:focus-within) .container {
26
- border-color: var(--md-sys-color-primary);
27
- outline-width: 3px;
45
+ :host([invalid]:hover) {
46
+ --_outline-color: var(--md-sys-color-on-error-container);
47
+ --_border-color: var(--md-sys-color-on-error-container);
28
48
  }
29
49
 
30
50
  .label {