@api-client/ui 0.3.3 → 0.3.5

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 (50) hide show
  1. package/build/src/md/button/internals/base.d.ts.map +1 -1
  2. package/build/src/md/button/internals/base.js +2 -6
  3. package/build/src/md/button/internals/base.js.map +1 -1
  4. package/build/src/md/button/internals/button.styles.js +1 -1
  5. package/build/src/md/button/internals/button.styles.js.map +1 -1
  6. package/build/src/md/chip/internals/Chip.d.ts +11 -15
  7. package/build/src/md/chip/internals/Chip.d.ts.map +1 -1
  8. package/build/src/md/chip/internals/Chip.js +66 -104
  9. package/build/src/md/chip/internals/Chip.js.map +1 -1
  10. package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
  11. package/build/src/md/chip/internals/Chip.styles.js +114 -101
  12. package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
  13. package/build/src/md/chip/internals/ChipSet.d.ts +16 -0
  14. package/build/src/md/chip/internals/ChipSet.d.ts.map +1 -0
  15. package/build/src/md/chip/internals/ChipSet.js +138 -0
  16. package/build/src/md/chip/internals/ChipSet.js.map +1 -0
  17. package/build/src/md/chip/internals/ChipSet.styles.d.ts +3 -0
  18. package/build/src/md/chip/internals/ChipSet.styles.d.ts.map +1 -0
  19. package/build/src/md/chip/internals/ChipSet.styles.js +9 -0
  20. package/build/src/md/chip/internals/ChipSet.styles.js.map +1 -0
  21. package/build/src/md/chip/ui-chip-set.d.ts +11 -0
  22. package/build/src/md/chip/ui-chip-set.d.ts.map +1 -0
  23. package/build/src/md/chip/ui-chip-set.js +27 -0
  24. package/build/src/md/chip/ui-chip-set.js.map +1 -0
  25. package/build/src/md/dialog/internals/Dialog.styles.d.ts.map +1 -1
  26. package/build/src/md/dialog/internals/Dialog.styles.js +5 -11
  27. package/build/src/md/dialog/internals/Dialog.styles.js.map +1 -1
  28. package/build/src/md/switch/internals/Switch.styles.js +1 -1
  29. package/build/src/md/switch/internals/Switch.styles.js.map +1 -1
  30. package/build/src/md/text-field/internals/outlined.styles.js +2 -2
  31. package/build/src/md/text-field/internals/outlined.styles.js.map +1 -1
  32. package/demo/md/chip/chip.html +33 -6
  33. package/demo/md/chip/chip.ts +111 -56
  34. package/package.json +2 -2
  35. package/src/md/button/internals/base.ts +2 -6
  36. package/src/md/button/internals/button.styles.ts +1 -1
  37. package/src/md/chip/internals/Chip.styles.ts +114 -101
  38. package/src/md/chip/internals/Chip.ts +58 -88
  39. package/src/md/chip/internals/ChipSet.styles.ts +9 -0
  40. package/src/md/chip/internals/ChipSet.ts +142 -0
  41. package/src/md/chip/ui-chip-set.ts +15 -0
  42. package/src/md/dialog/internals/Dialog.styles.ts +5 -11
  43. package/src/md/switch/internals/Switch.styles.ts +1 -1
  44. package/src/md/text-field/internals/outlined.styles.ts +2 -2
  45. package/test/ui/chip/UiChip.test.ts +18 -67
  46. package/build/src/events/BroadcastUiEvents.d.ts +0 -18
  47. package/build/src/events/BroadcastUiEvents.d.ts.map +0 -1
  48. package/build/src/events/BroadcastUiEvents.js +0 -2
  49. package/build/src/events/BroadcastUiEvents.js.map +0 -1
  50. package/src/events/BroadcastUiEvents.ts +0 -19
@@ -0,0 +1,142 @@
1
+ import { html, LitElement } from 'lit'
2
+ import { queryAssignedElements } from 'lit/decorators.js'
3
+
4
+ import Chip from './Chip.js'
5
+
6
+ /**
7
+ * A chip set component.
8
+ */
9
+ export default class ChipSet extends LitElement {
10
+ get chips() {
11
+ return this.childElements.filter((child): child is Chip => child instanceof Chip)
12
+ }
13
+
14
+ @queryAssignedElements() private accessor childElements!: HTMLElement[]
15
+ private readonly internals = this.attachInternals()
16
+
17
+ constructor() {
18
+ super()
19
+ this.addEventListener('focusin', this.updateTabIndices.bind(this))
20
+ this.addEventListener('update-focus', this.updateTabIndices.bind(this))
21
+ this.addEventListener('keydown', this.handleKeyDown.bind(this))
22
+ this.internals.role = 'toolbar'
23
+ }
24
+
25
+ override connectedCallback(): void {
26
+ super.connectedCallback()
27
+ if (!this.hasAttribute('role')) {
28
+ this.setAttribute('role', 'toolbar')
29
+ }
30
+ }
31
+
32
+ protected override render() {
33
+ return html`<slot @slotchange=${this.updateTabIndices}></slot>`
34
+ }
35
+
36
+ private handleKeyDown(event: KeyboardEvent) {
37
+ const isLeft = event.key === 'ArrowLeft'
38
+ const isRight = event.key === 'ArrowRight'
39
+ const isHome = event.key === 'Home'
40
+ const isEnd = event.key === 'End'
41
+ // Ignore non-navigation keys
42
+ if (!isLeft && !isRight && !isHome && !isEnd) {
43
+ return
44
+ }
45
+
46
+ const { chips } = this as { chips: MaybeMultiActionChip[] }
47
+ // Don't try to select another chip if there aren't any.
48
+ if (chips.length < 2) {
49
+ return
50
+ }
51
+
52
+ // Prevent default interactions, such as scrolling.
53
+ event.preventDefault()
54
+
55
+ if (isHome || isEnd) {
56
+ const index = isHome ? 0 : chips.length - 1
57
+ chips[index].focus({ trailing: isEnd })
58
+ this.updateTabIndices()
59
+ return
60
+ }
61
+
62
+ // Check if moving forwards or backwards
63
+ const isRtl = getComputedStyle(this).direction === 'rtl'
64
+ const forwards = isRtl ? isLeft : isRight
65
+ const focusedChip = chips.find((chip) => chip.matches(':focus-within'))
66
+ if (!focusedChip) {
67
+ // If there is not already a chip focused, select the first or last chip
68
+ // based on the direction we're traveling.
69
+ const nextChip = forwards ? chips[0] : chips[chips.length - 1]
70
+ nextChip.focus({ trailing: !forwards })
71
+ this.updateTabIndices()
72
+ return
73
+ }
74
+
75
+ const currentIndex = chips.indexOf(focusedChip)
76
+ let nextIndex = forwards ? currentIndex + 1 : currentIndex - 1
77
+ // Search for the next sibling that is not disabled to select.
78
+ // If we return to the host index, there is nothing to select.
79
+ while (nextIndex !== currentIndex) {
80
+ if (nextIndex >= chips.length) {
81
+ // Return to start if moving past the last item.
82
+ nextIndex = 0
83
+ } else if (nextIndex < 0) {
84
+ // Go to end if moving before the first item.
85
+ nextIndex = chips.length - 1
86
+ }
87
+
88
+ // Check if the next sibling is disabled. If so,
89
+ // move the index and continue searching.
90
+ //
91
+ // Some toolbar items may be focusable when disabled for increased
92
+ // visibility.
93
+ const nextChip = chips[nextIndex]
94
+ if (nextChip.disabled) {
95
+ if (forwards) {
96
+ nextIndex++
97
+ } else {
98
+ nextIndex--
99
+ }
100
+
101
+ continue
102
+ }
103
+
104
+ nextChip.focus({ trailing: !forwards })
105
+ this.updateTabIndices()
106
+ break
107
+ }
108
+ }
109
+
110
+ private updateTabIndices() {
111
+ // The chip that should be focusable is either the chip that currently has
112
+ // focus or the first chip that can be focused.
113
+ const { chips } = this
114
+ let chipToFocus: Chip | undefined
115
+ for (const chip of chips) {
116
+ const isChipFocusable = !chip.disabled
117
+ const chipIsFocused = chip.matches(':focus-within')
118
+ if (chipIsFocused && isChipFocusable) {
119
+ // Found the first chip that is actively focused. This overrides the
120
+ // first focusable chip found.
121
+ chipToFocus = chip
122
+ continue
123
+ }
124
+
125
+ if (isChipFocusable && !chipToFocus) {
126
+ chipToFocus = chip
127
+ }
128
+
129
+ // Disable non-focused chips. If we disable all of them, we'll grant focus
130
+ // to the first focusable child that was found.
131
+ chip.tabIndex = -1
132
+ }
133
+
134
+ if (chipToFocus) {
135
+ chipToFocus.tabIndex = 0
136
+ }
137
+ }
138
+ }
139
+
140
+ interface MaybeMultiActionChip extends Chip {
141
+ focus(options?: FocusOptions & { trailing?: boolean }): void
142
+ }
@@ -0,0 +1,15 @@
1
+ import type { CSSResultOrNative } from 'lit'
2
+ import { customElement } from 'lit/decorators.js'
3
+ import Element from './internals/ChipSet.js'
4
+ import styles from './internals/ChipSet.styles.js'
5
+
6
+ @customElement('ui-chip-set')
7
+ export class UiChipSetElement extends Element {
8
+ static override styles: CSSResultOrNative[] = [styles]
9
+ }
10
+
11
+ declare global {
12
+ interface HTMLElementTagNameMap {
13
+ 'ui-chip-set': UiChipSetElement
14
+ }
15
+ }
@@ -11,7 +11,7 @@ export default css`
11
11
 
12
12
  border: none;
13
13
  border-radius: var(--md-sys-shape-corner-extra-large);
14
- background-color: var(--md-sys-color-surface);
14
+ background-color: var(--md-sys-color-surface-container-high);
15
15
  box-shadow: var(--md-sys-elevation-3);
16
16
  color: var(--md-sys-color-on-surface-variant);
17
17
  padding: 24px;
@@ -22,16 +22,6 @@ export default css`
22
22
  height: var(--ui-dialog-height, revert);
23
23
  }
24
24
 
25
- dialog::after {
26
- content: '';
27
- position: absolute;
28
- inset: 0;
29
- border-radius: inherit;
30
- background-color: var(--md-sys-color-surface-tint);
31
- opacity: 0.11;
32
- pointer-events: none;
33
- }
34
-
35
25
  dialog:open {
36
26
  animation: 250ms cubic-bezier(0.2, 0, 0, 1) show-dialog;
37
27
  }
@@ -107,6 +97,10 @@ export default css`
107
97
  margin-right: 12px;
108
98
  }
109
99
 
100
+ .content ::slotted(*) {
101
+ background-color: var(--md-sys-color-surface-container-high);
102
+ }
103
+
110
104
  @keyframes show-dialog {
111
105
  from {
112
106
  transform: translateY(-110%) scaleY(0);
@@ -35,7 +35,7 @@ export default css`
35
35
 
36
36
  border: 2px var(--md-sys-color-outline) solid;
37
37
  border-radius: var(--md-sys-shape-corner-extra-large);
38
- background-color: var(--md-sys-color-surface-variant);
38
+ background-color: var(--md-sys-color-surface-container-highest);
39
39
 
40
40
  transition:
41
41
  opacity 90ms cubic-bezier(0.4, 0, 0.2, 1),
@@ -54,10 +54,10 @@ export default css`
54
54
  .labelActive .label {
55
55
  transform: translateY(calc(-100% - 2px)) scale(0.75);
56
56
  position: absolute;
57
- background-color: var(--md-sys-color-surface);
57
+ background-color: var(--md-outlined-text-field-label-active-background-color, var(--md-sys-color-surface));
58
58
  }
59
59
 
60
60
  :host(:focus-within) .label {
61
- color: var(--md-sys-color-primary);
61
+ color: var(--md-outlined-text-field-label-active-color, var(--md-sys-color-primary));
62
62
  }
63
63
  `
@@ -26,15 +26,6 @@ describe('md', () => {
26
26
  )
27
27
  }
28
28
 
29
- async function disableIconFixture(): Promise<UiChip> {
30
- return fixture(
31
- html`<ui-chip type="assist" disabled>
32
- <ui-icon slot="icon" icon="add"></ui-icon>
33
- Disabled with icon
34
- </ui-chip>`
35
- )
36
- }
37
-
38
29
  async function elevatedFixture(): Promise<UiChip> {
39
30
  return fixture(html`<ui-chip type="assist" elevated>Elevated</ui-chip>`)
40
31
  }
@@ -45,9 +36,8 @@ describe('md', () => {
45
36
 
46
37
  it('renders border around a regular assist chip', async () => {
47
38
  const element = await basicFixture()
48
- const container = element.shadowRoot?.querySelector('.container') as HTMLDivElement
49
- const styles = getComputedStyle(container)
50
- const m3Color = styles.getPropertyValue('--md-sys-color-outline')
39
+ const styles = getComputedStyle(element)
40
+ const m3Color = styles.getPropertyValue('--md-sys-color-outline-variant')
51
41
  const borderColor = ColorConverter.normalizeColor(styles.borderColor)
52
42
  const compareColor = ColorConverter.normalizeColor(m3Color)
53
43
  assert.equal(borderColor, compareColor, 'has the m3 color')
@@ -89,43 +79,28 @@ describe('md', () => {
89
79
 
90
80
  it('renders suffix icon', async () => {
91
81
  const element = await iconFixture()
92
- const surface = element.shadowRoot!.querySelector('.surface') as HTMLDivElement
93
- assert.isTrue(surface.classList.contains('withIcon'), 'the surface has the withIcon class')
94
- const content = element.shadowRoot!.querySelector('.content') as HTMLDivElement
95
- const contentStyles = getComputedStyle(content)
82
+ const surface = element.shadowRoot!.querySelector<HTMLDivElement>('.surface')!
83
+ assert.isTrue(surface.classList.contains('has-icon'), 'the surface has the has-icon class')
84
+ const contentStyles = getComputedStyle(surface)
96
85
  assert.equal(contentStyles.paddingLeft.trim(), '8px', 'the container has the padding')
97
86
  const icon = element.querySelector('ui-icon')!
98
87
  const iconStyles = getComputedStyle(icon)
99
88
  assert.equal(iconStyles.marginRight.trim(), '8px', 'the icon has the margin')
100
89
  })
101
90
 
102
- it('has disabled styles when disabled', async () => {
103
- const element = await disableIconFixture()
104
- const container = element.shadowRoot!.querySelector('.container') as HTMLDivElement
105
- const containerStyles = getComputedStyle(container)
106
- assert.equal(containerStyles.opacity, '0.12', 'container has the opacity')
107
- const content = element.shadowRoot!.querySelector('.content') as HTMLDivElement
108
- const contentStyles = getComputedStyle(content)
109
- assert.equal(contentStyles.opacity, '0.68', 'content has the opacity')
110
- })
111
-
112
91
  it('has elevated styles', async () => {
113
92
  const element = await elevatedFixture()
114
- const container = element.shadowRoot!.querySelector('.container') as HTMLDivElement
115
- const containerStyles = getComputedStyle(container)
93
+ const surface = element.shadowRoot!.querySelector<HTMLDivElement>('.surface')!
94
+ const containerStyles = getComputedStyle(surface)
116
95
  assert.equal(containerStyles.borderWidth, '0px', 'has no border')
117
96
  assert.isNotEmpty(containerStyles.boxShadow, 'has a box shadow')
118
97
  })
119
98
 
120
99
  it('has no elevated styles when disabled', async () => {
121
100
  const element = await disabledElevatedFixture()
122
- const container = element.shadowRoot!.querySelector('.container') as HTMLDivElement
123
- const containerStyles = getComputedStyle(container)
101
+ const surface = element.shadowRoot!.querySelector<HTMLDivElement>('.surface')!
102
+ const containerStyles = getComputedStyle(surface)
124
103
  assert.equal(containerStyles.boxShadow, 'none', 'has no box shadow')
125
- assert.equal(containerStyles.opacity, '0.12', 'container has the opacity')
126
- const content = element.shadowRoot!.querySelector('.content') as HTMLDivElement
127
- const contentStyles = getComputedStyle(content)
128
- assert.equal(contentStyles.opacity, '0.68', 'content has the opacity')
129
104
  })
130
105
  })
131
106
 
@@ -138,19 +113,18 @@ describe('md', () => {
138
113
  return fixture(html`<ui-chip type="filter" disabled>Disabled</ui-chip>`)
139
114
  }
140
115
 
141
- async function closableFixture(): Promise<UiChip> {
142
- return fixture(html`<ui-chip type="filter" closable>Closable</ui-chip>`)
116
+ async function removableFixture(): Promise<UiChip> {
117
+ return fixture(html`<ui-chip type="filter" removable>removable</ui-chip>`)
143
118
  }
144
119
 
145
120
  async function listFixture(): Promise<UiChip> {
146
- return fixture(html`<ui-chip type="filter" list="abc">List</ui-chip>`)
121
+ return fixture(html`<ui-chip type="filter" list>List</ui-chip>`)
147
122
  }
148
123
 
149
- it('renders a border around the chip', async () => {
124
+ it('renders border around the chip', async () => {
150
125
  const element = await basicFixture()
151
- const container = element.shadowRoot?.querySelector('.container') as HTMLDivElement
152
- const styles = getComputedStyle(container)
153
- const m3Color = styles.getPropertyValue('--md-sys-color-outline')
126
+ const styles = getComputedStyle(element)
127
+ const m3Color = styles.getPropertyValue('--md-sys-color-outline-variant')
154
128
  const borderColor = ColorConverter.normalizeColor(styles.borderColor)
155
129
  const compareColor = ColorConverter.normalizeColor(m3Color)
156
130
  assert.equal(borderColor, compareColor, 'has the m3 color')
@@ -208,7 +182,7 @@ describe('md', () => {
208
182
  })
209
183
 
210
184
  it('does not render the close icon', async () => {
211
- const element = await closableFixture()
185
+ const element = await removableFixture()
212
186
  const node = element.shadowRoot!.querySelector('.trailing-icon')
213
187
  assert.notOk(node)
214
188
  })
@@ -219,36 +193,13 @@ describe('md', () => {
219
193
  assert.ok(node, 'has the icon')
220
194
  })
221
195
 
222
- it('dispatches the list event', async () => {
223
- const element = await listFixture()
224
- const spy = sinon.spy()
225
- element.addEventListener('list', spy)
226
- const node = element.shadowRoot!.querySelector('.trailing-icon') as HTMLElement
227
- node.click()
228
- assert.isTrue(spy.calledOnce)
229
- })
230
-
231
- it('prevents the "click" event when dispatching the "list" event', async () => {
232
- const element = await listFixture()
233
- const spy = sinon.spy()
234
- element.addEventListener('click', spy)
235
- const node = element.shadowRoot!.querySelector('.trailing-icon') as HTMLElement
236
- node.click()
237
- assert.isFalse(spy.called)
238
- })
239
-
240
196
  it('is accessible in a regular state', async () => {
241
197
  const element = await basicFixture()
242
198
  await assert.isAccessible(element)
243
199
  })
244
200
 
245
- it('is accessible in a disabled state', async () => {
246
- const element = await disabledFixture()
247
- await assert.isAccessible(element)
248
- })
249
-
250
- it('is accessible when closable', async () => {
251
- const element = await closableFixture()
201
+ it('is accessible when removable', async () => {
202
+ const element = await removableFixture()
252
203
  await assert.isAccessible(element)
253
204
  })
254
205
 
@@ -1,18 +0,0 @@
1
- import { BroadcastCreatedEvent, DeletedBroadcastEvent } from '@api-client/core/models/store/Backend.js';
2
- import { type IRequestUiMeta, Kind as RequestUiMetaKind } from '@api-client/core/models/RequestUiMeta.js';
3
- /**
4
- * @deprecated
5
- */
6
- export interface ProjectUiCreatedBroadcastEvent extends BroadcastCreatedEvent {
7
- project: string;
8
- kind: typeof RequestUiMetaKind;
9
- data: IRequestUiMeta;
10
- }
11
- /**
12
- * @deprecated
13
- */
14
- export interface ProjectUiDeletedBroadcastEvent extends DeletedBroadcastEvent {
15
- project: string;
16
- kind: typeof RequestUiMetaKind;
17
- }
18
- //# sourceMappingURL=BroadcastUiEvents.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BroadcastUiEvents.d.ts","sourceRoot":"","sources":["../../../src/events/BroadcastUiEvents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAA;AACvG,OAAO,EAAE,KAAK,cAAc,EAAE,IAAI,IAAI,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAEzG;;GAEG;AACH,MAAM,WAAW,8BAA+B,SAAQ,qBAAqB;IAC3E,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,iBAAiB,CAAA;IAC9B,IAAI,EAAE,cAAc,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA+B,SAAQ,qBAAqB;IAC3E,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,iBAAiB,CAAA;CAC/B"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=BroadcastUiEvents.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BroadcastUiEvents.js","sourceRoot":"","sources":["../../../src/events/BroadcastUiEvents.ts"],"names":[],"mappings":"","sourcesContent":["import { BroadcastCreatedEvent, DeletedBroadcastEvent } from '@api-client/core/models/store/Backend.js'\nimport { type IRequestUiMeta, Kind as RequestUiMetaKind } from '@api-client/core/models/RequestUiMeta.js'\n\n/**\n * @deprecated\n */\nexport interface ProjectUiCreatedBroadcastEvent extends BroadcastCreatedEvent {\n project: string\n kind: typeof RequestUiMetaKind\n data: IRequestUiMeta\n}\n\n/**\n * @deprecated\n */\nexport interface ProjectUiDeletedBroadcastEvent extends DeletedBroadcastEvent {\n project: string\n kind: typeof RequestUiMetaKind\n}\n"]}
@@ -1,19 +0,0 @@
1
- import { BroadcastCreatedEvent, DeletedBroadcastEvent } from '@api-client/core/models/store/Backend.js'
2
- import { type IRequestUiMeta, Kind as RequestUiMetaKind } from '@api-client/core/models/RequestUiMeta.js'
3
-
4
- /**
5
- * @deprecated
6
- */
7
- export interface ProjectUiCreatedBroadcastEvent extends BroadcastCreatedEvent {
8
- project: string
9
- kind: typeof RequestUiMetaKind
10
- data: IRequestUiMeta
11
- }
12
-
13
- /**
14
- * @deprecated
15
- */
16
- export interface ProjectUiDeletedBroadcastEvent extends DeletedBroadcastEvent {
17
- project: string
18
- kind: typeof RequestUiMetaKind
19
- }