@dxos/lit-ui 0.8.1-staging.31c3ee1

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 (88) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +3 -0
  3. package/dist/src/defs.d.ts +2 -0
  4. package/dist/src/defs.d.ts.map +1 -0
  5. package/dist/src/defs.js +4 -0
  6. package/dist/src/defs.js.map +1 -0
  7. package/dist/src/dx-avatar/dx-avatar.d.ts +32 -0
  8. package/dist/src/dx-avatar/dx-avatar.d.ts.map +1 -0
  9. package/dist/src/dx-avatar/dx-avatar.js +191 -0
  10. package/dist/src/dx-avatar/dx-avatar.js.map +1 -0
  11. package/dist/src/dx-avatar/dx-avatar.lit-stories.d.ts +12 -0
  12. package/dist/src/dx-avatar/dx-avatar.lit-stories.d.ts.map +1 -0
  13. package/dist/src/dx-avatar/dx-avatar.lit-stories.js +16 -0
  14. package/dist/src/dx-avatar/dx-avatar.lit-stories.js.map +1 -0
  15. package/dist/src/dx-avatar/index.d.ts +2 -0
  16. package/dist/src/dx-avatar/index.d.ts.map +1 -0
  17. package/dist/src/dx-avatar/index.js +5 -0
  18. package/dist/src/dx-avatar/index.js.map +1 -0
  19. package/dist/src/dx-icon/dx-icon.d.ts +10 -0
  20. package/dist/src/dx-icon/dx-icon.d.ts.map +1 -0
  21. package/dist/src/dx-icon/dx-icon.js +43 -0
  22. package/dist/src/dx-icon/dx-icon.js.map +1 -0
  23. package/dist/src/dx-icon/index.d.ts +2 -0
  24. package/dist/src/dx-icon/index.d.ts.map +1 -0
  25. package/dist/src/dx-icon/index.js +5 -0
  26. package/dist/src/dx-icon/index.js.map +1 -0
  27. package/dist/src/dx-tag-picker/dx-tag-picker-item.d.ts +21 -0
  28. package/dist/src/dx-tag-picker/dx-tag-picker-item.d.ts.map +1 -0
  29. package/dist/src/dx-tag-picker/dx-tag-picker-item.js +68 -0
  30. package/dist/src/dx-tag-picker/dx-tag-picker-item.js.map +1 -0
  31. package/dist/src/dx-tag-picker/index.d.ts +2 -0
  32. package/dist/src/dx-tag-picker/index.d.ts.map +1 -0
  33. package/dist/src/dx-tag-picker/index.js +5 -0
  34. package/dist/src/dx-tag-picker/index.js.map +1 -0
  35. package/dist/src/index.d.ts +5 -0
  36. package/dist/src/index.d.ts.map +1 -0
  37. package/dist/src/index.js +8 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/tsconfig.tsbuildinfo +1 -0
  40. package/dist/types/src/defs.d.ts +2 -0
  41. package/dist/types/src/defs.d.ts.map +1 -0
  42. package/dist/types/src/defs.js +4 -0
  43. package/dist/types/src/defs.js.map +1 -0
  44. package/dist/types/src/dx-avatar/dx-avatar.d.ts +32 -0
  45. package/dist/types/src/dx-avatar/dx-avatar.d.ts.map +1 -0
  46. package/dist/types/src/dx-avatar/dx-avatar.js +191 -0
  47. package/dist/types/src/dx-avatar/dx-avatar.js.map +1 -0
  48. package/dist/types/src/dx-avatar/dx-avatar.lit-stories.d.ts +12 -0
  49. package/dist/types/src/dx-avatar/dx-avatar.lit-stories.d.ts.map +1 -0
  50. package/dist/types/src/dx-avatar/dx-avatar.lit-stories.js +16 -0
  51. package/dist/types/src/dx-avatar/dx-avatar.lit-stories.js.map +1 -0
  52. package/dist/types/src/dx-avatar/index.d.ts +2 -0
  53. package/dist/types/src/dx-avatar/index.d.ts.map +1 -0
  54. package/dist/types/src/dx-avatar/index.js +5 -0
  55. package/dist/types/src/dx-avatar/index.js.map +1 -0
  56. package/dist/types/src/dx-icon/dx-icon.d.ts +10 -0
  57. package/dist/types/src/dx-icon/dx-icon.d.ts.map +1 -0
  58. package/dist/types/src/dx-icon/dx-icon.js +43 -0
  59. package/dist/types/src/dx-icon/dx-icon.js.map +1 -0
  60. package/dist/types/src/dx-icon/index.d.ts +2 -0
  61. package/dist/types/src/dx-icon/index.d.ts.map +1 -0
  62. package/dist/types/src/dx-icon/index.js +5 -0
  63. package/dist/types/src/dx-icon/index.js.map +1 -0
  64. package/dist/types/src/dx-tag-picker/dx-tag-picker-item.d.ts +21 -0
  65. package/dist/types/src/dx-tag-picker/dx-tag-picker-item.d.ts.map +1 -0
  66. package/dist/types/src/dx-tag-picker/dx-tag-picker-item.js +68 -0
  67. package/dist/types/src/dx-tag-picker/dx-tag-picker-item.js.map +1 -0
  68. package/dist/types/src/dx-tag-picker/index.d.ts +2 -0
  69. package/dist/types/src/dx-tag-picker/index.d.ts.map +1 -0
  70. package/dist/types/src/dx-tag-picker/index.js +5 -0
  71. package/dist/types/src/dx-tag-picker/index.js.map +1 -0
  72. package/dist/types/src/index.d.ts +5 -0
  73. package/dist/types/src/index.d.ts.map +1 -0
  74. package/dist/types/src/index.js +8 -0
  75. package/dist/types/src/index.js.map +1 -0
  76. package/dist/types/tsconfig.tsbuildinfo +1 -0
  77. package/package.json +34 -0
  78. package/src/defs.ts +43 -0
  79. package/src/dx-avatar/dx-avatar.lit-stories.ts +20 -0
  80. package/src/dx-avatar/dx-avatar.pcss +78 -0
  81. package/src/dx-avatar/dx-avatar.ts +214 -0
  82. package/src/dx-avatar/index.ts +5 -0
  83. package/src/dx-icon/dx-icon.ts +33 -0
  84. package/src/dx-icon/index.ts +5 -0
  85. package/src/dx-tag-picker/dx-tag-picker-item.ts +59 -0
  86. package/src/dx-tag-picker/dx-tag-picker.pcss +23 -0
  87. package/src/dx-tag-picker/index.ts +5 -0
  88. package/src/index.ts +9 -0
@@ -0,0 +1,78 @@
1
+ .dx-avatar-group {
2
+ @apply inline-flex items-center;
3
+ }
4
+
5
+ .dx-avatar-group .dx-avatar {
6
+ @apply -mie-2
7
+ }
8
+
9
+ .dx-avatar-group .dx-avatar {
10
+ &[size="0"], &[size="px"], &[size="0.5"], &[size="1"], &[size="1.5"], &[size="2"], &[size="2.5"] {
11
+ @apply -mie-1
12
+ }
13
+ }
14
+
15
+ dx-avatar {
16
+ display: contents;
17
+ }
18
+
19
+ .dx-avatar {
20
+ @apply relative inline-flex shrink-0;
21
+
22
+ .dx-avatar__frame {
23
+ @apply is-full bs-full bg-[--surface-bg]
24
+ }
25
+
26
+ .dx-avatar__frame, .dx-avatar__ring {
27
+ @apply rounded-full;
28
+ }
29
+
30
+ &[data-variant="square"] {
31
+ .dx-avatar__frame, .dx-avatar__ring {
32
+ @apply rounded-sm;
33
+ }
34
+ }
35
+
36
+ .dx-avatar__status-icon {
37
+ @apply absolute block-end-0 inline-end-0;
38
+ }
39
+
40
+ .dx-avatar__ring {
41
+ @apply absolute inset-0 border-2 border-[color:var(--surface-bg)];
42
+ }
43
+
44
+ &[data-status="current"] .dx-avatar__ring {
45
+ @apply border-[color:var(--dx-currentCursor)];
46
+ }
47
+
48
+ &[data-status="active"] .dx-avatar__ring {
49
+ @apply border-[color:var(--dx-activeCursor)];
50
+ }
51
+
52
+ &[data-status="error"] .dx-avatar__ring {
53
+ @apply border-[color:var(--dx-errorCursor)];
54
+ }
55
+
56
+ &[data-status="warning"] .dx-avatar__ring {
57
+ @apply border-[color:var(--dx-warningCursor)];
58
+ }
59
+
60
+ &[data-status="internal"] .dx-avatar__ring {
61
+ @apply border-[color:var(--dx-internalCursor)];
62
+ }
63
+
64
+ &[data-status="inactive"] .dx-avatar__ring {
65
+ @apply border-separator;
66
+ }
67
+
68
+ &[data-animation="pulse"] .dx-avatar__ring {
69
+ @apply animate-halo-pulse;
70
+ }
71
+
72
+ &[data-state-loading-status="loaded"] {
73
+ .dx-avatar__fallback-text, .dx-avatar__status-icon {
74
+ @apply hidden;
75
+ }
76
+ }
77
+ }
78
+
@@ -0,0 +1,214 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { html, svg, LitElement } from 'lit';
6
+ import { customElement, state, property } from 'lit/decorators.js';
7
+ import { styleMap } from 'lit/directives/style-map.js';
8
+
9
+ import { makeId } from '@dxos/react-hooks';
10
+
11
+ import { type Size } from '../defs';
12
+
13
+ export type ImageLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error';
14
+
15
+ export type AvatarVariant = 'square' | 'circle';
16
+ export type AvatarStatus = 'active' | 'inactive' | 'current' | 'error' | 'warning' | 'internal';
17
+ export type AvatarAnimation = 'pulse' | 'none';
18
+
19
+ const rx = '0.125rem';
20
+
21
+ export type DxAvatarProps = Partial<
22
+ Pick<
23
+ DxAvatar,
24
+ | 'fallback'
25
+ | 'labelId'
26
+ | 'imgSrc'
27
+ | 'imgCrossOrigin'
28
+ | 'imgReferrerPolicy'
29
+ | 'variant'
30
+ | 'status'
31
+ | 'animation'
32
+ | 'hue'
33
+ | 'hueVariant'
34
+ | 'size'
35
+ | 'icon'
36
+ | 'rootClassName'
37
+ >
38
+ >;
39
+
40
+ @customElement('dx-avatar')
41
+ export class DxAvatar extends LitElement {
42
+ private maskId: string;
43
+
44
+ constructor() {
45
+ super();
46
+ this.maskId = makeId('avatar__mask');
47
+ }
48
+
49
+ @property({ type: String })
50
+ fallback: string = 'never';
51
+
52
+ @property({ type: String })
53
+ labelId: string = 'never';
54
+
55
+ @property({ type: String })
56
+ imgSrc: string | undefined = undefined;
57
+
58
+ @property({ type: String })
59
+ imgCrossOrigin: NonNullable<HTMLImageElement['crossOrigin']> | undefined = undefined;
60
+
61
+ @property({ type: String })
62
+ imgReferrerPolicy: HTMLImageElement['referrerPolicy'] | undefined = undefined;
63
+
64
+ @property({ type: String })
65
+ variant: AvatarVariant = 'circle';
66
+
67
+ @property({ type: String })
68
+ status: AvatarStatus | undefined = undefined;
69
+
70
+ @property({ type: String })
71
+ animation: AvatarAnimation = 'none';
72
+
73
+ @property({ type: String })
74
+ hue: string | undefined = undefined;
75
+
76
+ @property({ type: String })
77
+ hueVariant: 'fill' | 'surface' = 'fill';
78
+
79
+ @property({ type: String })
80
+ size: Size = 10;
81
+
82
+ @property({ type: String })
83
+ icon: string | undefined = undefined;
84
+
85
+ @property({ type: String })
86
+ rootClassName: string | undefined = undefined;
87
+
88
+ @state()
89
+ loadingStaus: ImageLoadingStatus = 'idle';
90
+
91
+ override connectedCallback() {
92
+ super.connectedCallback();
93
+ this.loadingStaus = this.imgSrc ? 'loading' : 'idle';
94
+ }
95
+
96
+ override willUpdate(changedProperties: Map<string, any>) {
97
+ if (changedProperties.has('imgSrc')) {
98
+ this.loadingStaus = changedProperties.get('imgSrc') ? 'loading' : 'idle';
99
+ }
100
+ }
101
+
102
+ private handleLoad() {
103
+ this.loadingStaus = 'loaded';
104
+ }
105
+
106
+ private handleError() {
107
+ this.loadingStaus = 'error';
108
+ }
109
+
110
+ override render() {
111
+ const numericSize = this.size === 'px' ? 1 : Number(this.size);
112
+ const sizePx = numericSize * 4;
113
+ const ringWidth = this.status ? (numericSize > 4 ? 2 : numericSize > 3 ? 1 : 1) : 0;
114
+ const ringGap = this.status ? (numericSize > 12 ? 3 : numericSize > 4 ? 2 : numericSize > 3 ? 1 : 0) : 0;
115
+ const r = sizePx / 2 - ringGap - ringWidth;
116
+ const isTextOnly = Boolean(this.fallback && /[0-9a-zA-Z]+/.test(this.fallback));
117
+ const fontScale = (isTextOnly ? 3 : 3.6) * (1 / 1.612);
118
+ const bg = this.hue
119
+ ? this.hueVariant === 'surface'
120
+ ? `var(--dx-${this.hue}Surface)`
121
+ : `var(--dx-${this.hue}Fill)`
122
+ : 'var(--surface-bg)';
123
+ const fg = this.hue && this.hueVariant === 'surface' ? `var(--dx-${this.hue}SurfaceText)` : 'var(--dx-inverse)';
124
+ return html`<span
125
+ role="img"
126
+ class=${`dx-avatar${this.rootClassName ? ` ${this.rootClassName}` : ''}`}
127
+ aria-labelledby=${this.labelId}
128
+ data-size=${this.size}
129
+ data-variant=${this.variant}
130
+ data-status=${this.status}
131
+ data-animation=${this.animation}
132
+ data-state-loading-status=${this.loadingStaus}
133
+ >${svg`<svg
134
+ viewBox=${`0 0 ${sizePx} ${sizePx}`}
135
+ width=${sizePx}
136
+ height=${sizePx}
137
+ class="dx-avatar__frame"
138
+ >
139
+ <defs>
140
+ <mask id=${this.maskId}>
141
+ ${
142
+ this.variant === 'circle'
143
+ ? svg`<circle fill="white" cx="50%" cy="50%" r=${r} />`
144
+ : svg`<rect
145
+ fill="white"
146
+ width=${2 * r}
147
+ height=${2 * r}
148
+ x=${ringGap + ringWidth}
149
+ y=${ringGap + ringWidth}
150
+ rx=${rx}
151
+ />`
152
+ }
153
+ </mask>
154
+ </defs>
155
+ ${
156
+ this.variant === 'circle'
157
+ ? svg` <circle
158
+ cx="50%"
159
+ cy="50%"
160
+ r=${r}
161
+ fill=${bg}
162
+ />`
163
+ : svg` <rect
164
+ fill=${bg}
165
+ x=${ringGap + ringWidth}
166
+ y=${ringGap + ringWidth}
167
+ width=${2 * r}
168
+ height=${2 * r}
169
+ rx=${rx}
170
+ />`
171
+ }
172
+ ${
173
+ this.imgSrc &&
174
+ svg`<image
175
+ width="100%"
176
+ height="100%"
177
+ preserveAspectRatio="xMidYMid slice"
178
+ href=${this.imgSrc}
179
+ mask=${`url(#${this.maskId})`}
180
+ crossorigin=${this.imgCrossOrigin}
181
+ @load=${this.handleLoad}
182
+ @error=${this.handleError}
183
+ />`
184
+ }
185
+ ${
186
+ this.icon
187
+ ? svg`<use
188
+ class="dx-avatar__icon"
189
+ href=${this.icon}
190
+ x=${sizePx / 5}
191
+ y=${sizePx / 5}
192
+ width=${(3 * sizePx) / 5}
193
+ height=${(3 * sizePx) / 5} />`
194
+ : svg`<text
195
+ x="50%"
196
+ y="50%"
197
+ class="dx-avatar__fallback-text"
198
+ fill=${fg}
199
+ text-anchor="middle"
200
+ alignment-baseline="central"
201
+ font-size=${this.size === 'px' ? '200%' : this.size * fontScale}
202
+ mask=${`url(#${this.maskId})`}
203
+ >
204
+ ${this.fallback}
205
+ </text>`
206
+ }
207
+ </svg>`}<span role="none" class="dx-avatar__ring" style=${styleMap({ borderWidth: ringWidth + 'px' })}
208
+ /></span>`;
209
+ }
210
+
211
+ override createRenderRoot() {
212
+ return this;
213
+ }
214
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './dx-avatar';
@@ -0,0 +1,33 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { svg, LitElement } from 'lit';
6
+ import { customElement, property } from 'lit/decorators.js';
7
+
8
+ import { type Size } from '../defs';
9
+
10
+ const ICONS_URL = '/icons.svg';
11
+
12
+ @customElement('dx-icon')
13
+ export class DxIcon extends LitElement {
14
+ // TODO(thure): Get Hue type used in theme.
15
+ @property({ type: String })
16
+ size: Size = 4;
17
+
18
+ @property({ type: String })
19
+ icon: string = 'ph--placeholder--regular';
20
+
21
+ @property({ type: Boolean })
22
+ noCache: boolean = true;
23
+
24
+ override render() {
25
+ const url = this.noCache ? `${ICONS_URL}?nocache=${new Date().getMinutes()}` : ICONS_URL;
26
+ const href = `${url}#${this.icon}`;
27
+ return svg`<svg class="dx-icon" data-size=${this.size}><use href=${href} /></svg>`;
28
+ }
29
+
30
+ override createRenderRoot() {
31
+ return this;
32
+ }
33
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './dx-icon';
@@ -0,0 +1,59 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { html, LitElement } from 'lit';
6
+ import { customElement, property } from 'lit/decorators.js';
7
+
8
+ import { makeId } from '@dxos/react-hooks';
9
+
10
+ export class DxTagPickerItemClick extends Event {
11
+ public readonly itemId: string;
12
+ public readonly action: 'remove' | 'activate';
13
+ constructor(props: { itemId: string; action: 'remove' | 'activate' }) {
14
+ super('dx-tag-picker-item-click');
15
+ this.itemId = props.itemId;
16
+ this.action = props.action;
17
+ }
18
+ }
19
+
20
+ @customElement('dx-tag-picker-item')
21
+ export class DxTagPickerItem extends LitElement {
22
+ // TODO(thure): Get Hue type used in theme.
23
+ @property({ type: String })
24
+ hue: string = 'neutral';
25
+
26
+ @property({ type: String })
27
+ itemId: string = makeId('dx-tag-picker-item');
28
+
29
+ @property({ type: String })
30
+ label: string = 'never';
31
+
32
+ @property({ type: String })
33
+ rootClassName: string | undefined = undefined;
34
+
35
+ @property({ type: String })
36
+ removeLabel: string | undefined = undefined;
37
+
38
+ private handleClickActivate() {
39
+ this.dispatchEvent(new DxTagPickerItemClick({ itemId: this.itemId, action: 'activate' }));
40
+ }
41
+
42
+ private handleClickRemove() {
43
+ this.dispatchEvent(new DxTagPickerItemClick({ itemId: this.itemId, action: 'remove' }));
44
+ }
45
+
46
+ override render() {
47
+ const className = `dx-tag dx-tag-picker-item${this.rootClassName ? ` ${this.rootClassName}` : ''}`;
48
+ return html`<span class=${className} data-remove=${!!this.removeLabel} data-hue=${this.hue} id=${this.id}
49
+ ><button class="dx-focus-ring" @click=${this.handleClickActivate}>${this.label}</button>${this.removeLabel &&
50
+ html`<button class="dx-focus-ring" aria-label=${this.removeLabel} @click=${this.handleClickRemove}>
51
+ <dx-icon icon="ph--x--regular" />
52
+ </button>`}</span
53
+ >`;
54
+ }
55
+
56
+ override createRenderRoot() {
57
+ return this;
58
+ }
59
+ }
@@ -0,0 +1,23 @@
1
+ @layer dx-components {
2
+ dx-tag-picker-item {
3
+ display: contents;
4
+ }
5
+
6
+ .dx-tag-picker-item.dx-tag {
7
+ @apply p-0 inline-flex;
8
+
9
+ button {
10
+ @apply pli-2;
11
+ padding-block: var(--dx-tag-padding-block);
12
+ }
13
+
14
+ &[data-remove='true'] {
15
+ button:first-child {
16
+ @apply pie-0.5;
17
+ }
18
+ button:last-child {
19
+ @apply pis-0.5;
20
+ }
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './dx-tag-picker-item';
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './dx-avatar';
6
+ export * from './dx-tag-picker';
7
+ export * from './dx-icon';
8
+
9
+ export { createComponent, type EventName } from '@lit/react';