@api-client/ui 0.5.7 → 0.5.9

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 (27) hide show
  1. package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts +225 -0
  2. package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts.map +1 -0
  3. package/build/src/elements/mention-textarea/internals/MentionTextArea.js +1065 -0
  4. package/build/src/elements/mention-textarea/internals/MentionTextArea.js.map +1 -0
  5. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts +3 -0
  6. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts.map +1 -0
  7. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js +274 -0
  8. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js.map +1 -0
  9. package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts +13 -0
  10. package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts.map +1 -0
  11. package/build/src/elements/mention-textarea/ui-mention-textarea.js +28 -0
  12. package/build/src/elements/mention-textarea/ui-mention-textarea.js.map +1 -0
  13. package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
  14. package/build/src/md/chip/internals/Chip.styles.js +2 -0
  15. package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
  16. package/demo/elements/index.html +3 -0
  17. package/demo/elements/mention-textarea/index.html +19 -0
  18. package/demo/elements/mention-textarea/index.ts +205 -0
  19. package/package.json +2 -2
  20. package/src/elements/mention-textarea/internals/MentionTextArea.styles.ts +274 -0
  21. package/src/elements/mention-textarea/internals/MentionTextArea.ts +1068 -0
  22. package/src/elements/mention-textarea/ui-mention-textarea.ts +18 -0
  23. package/src/md/chip/internals/Chip.styles.ts +2 -0
  24. package/test/elements/http/CertificateAdd.test.ts +0 -3
  25. package/test/elements/mention-textarea/MentionTextArea.basic.test.ts +114 -0
  26. package/test/elements/mention-textarea/MentionTextArea.test.ts +613 -0
  27. package/tsconfig.json +1 -1
@@ -0,0 +1,205 @@
1
+ import { html, TemplateResult } from 'lit'
2
+ import reactive from '../../../src/decorators/reactive.js'
3
+ import { DemoPage } from '../../../src/demo/DemoPage.js'
4
+ import type {
5
+ MentionTextAreaElement,
6
+ MentionSuggestion,
7
+ MentionInsertEvent,
8
+ MentionRemoveEvent,
9
+ } from '../../../src/elements/mention-textarea/ui-mention-textarea.js'
10
+ import '../../../src/elements/mention-textarea/ui-mention-textarea.js'
11
+
12
+ class ComponentDemoPage extends DemoPage {
13
+ override accessor componentName = 'Mention Textarea'
14
+
15
+ @reactive() accessor value = 'Hello @{john-doe}, please review the document with @{jane-smith}.'
16
+
17
+ @reactive() accessor suggestions: MentionSuggestion[] = [
18
+ {
19
+ id: 'john-doe',
20
+ label: 'John Doe',
21
+ description: 'Senior Developer',
22
+ suffix: 'Engineering',
23
+ },
24
+ {
25
+ id: 'jane-smith',
26
+ label: 'Jane Smith',
27
+ description: 'Product Manager',
28
+ suffix: 'Product',
29
+ },
30
+ {
31
+ id: 'bob-wilson',
32
+ label: 'Bob Wilson',
33
+ description: 'UI/UX Designer',
34
+ suffix: 'Design',
35
+ },
36
+ {
37
+ id: 'alice-johnson',
38
+ label: 'Alice Johnson',
39
+ description: 'Quality Assurance Engineer',
40
+ suffix: 'QA',
41
+ },
42
+ {
43
+ id: 'mike-brown',
44
+ label: 'Mike Brown',
45
+ description: 'DevOps Engineer',
46
+ suffix: 'Infrastructure',
47
+ },
48
+ {
49
+ id: 'sarah-davis',
50
+ label: 'Sarah Davis',
51
+ description: 'Technical Writer',
52
+ suffix: 'Documentation',
53
+ },
54
+ ]
55
+
56
+ handleMentionInsert(event: CustomEvent<MentionInsertEvent>): void {
57
+ console.log('Mention inserted:', event.detail)
58
+ }
59
+
60
+ handleMentionRemove(event: CustomEvent<MentionRemoveEvent>): void {
61
+ console.log('Mention removed:', event.detail)
62
+ }
63
+
64
+ handleInput(event: Event): void {
65
+ const target = event.target as MentionTextAreaElement
66
+ this.value = target.value
67
+ console.log('Value changed:', this.value)
68
+ }
69
+
70
+ contentTemplate(): TemplateResult {
71
+ return html`
72
+ <a href="../">Back</a>
73
+
74
+ <section class="demo-section">
75
+ <h2 class="display-large">Basic Usage</h2>
76
+ <p>
77
+ Type @ to trigger mentions. The component supports rich suggestions with headline, description, and suffix.
78
+ </p>
79
+
80
+ <mention-textarea
81
+ label="Message"
82
+ supporting-text="Type @ to mention someone"
83
+ .value="${this.value}"
84
+ .suggestions="${this.suggestions}"
85
+ @mention-insert="${this.handleMentionInsert}"
86
+ @mention-remove="${this.handleMentionRemove}"
87
+ @input="${this.handleInput}"
88
+ ></mention-textarea>
89
+
90
+ <h3 class="title-large">Current Value:</h3>
91
+ <pre
92
+ style="background: var(--md-sys-color-surface-variant); padding: 16px; border-radius: 8px; overflow-x: auto;"
93
+ >
94
+ ${this.value}
95
+ </pre
96
+ >
97
+ </section>
98
+
99
+ <section class="demo-section">
100
+ <h2 class="display-large">Different States</h2>
101
+
102
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
103
+ <mention-textarea label="Default" .suggestions="${this.suggestions}"></mention-textarea>
104
+
105
+ <mention-textarea
106
+ label="With placeholder"
107
+ placeholder="Start typing..."
108
+ .suggestions="${this.suggestions}"
109
+ ></mention-textarea>
110
+ </div>
111
+
112
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
113
+ <mention-textarea label="Required" required .suggestions="${this.suggestions}"></mention-textarea>
114
+
115
+ <mention-textarea
116
+ label="Invalid"
117
+ invalid
118
+ supporting-text="This field has an error"
119
+ .suggestions="${this.suggestions}"
120
+ ></mention-textarea>
121
+ </div>
122
+
123
+ <mention-textarea
124
+ label="Disabled"
125
+ disabled
126
+ value="This is disabled"
127
+ .suggestions="${this.suggestions}"
128
+ ></mention-textarea>
129
+ </section>
130
+
131
+ <section class="demo-section">
132
+ <h2 class="display-large">Custom Trigger</h2>
133
+ <p>This example uses '#' as the mention trigger instead of '@'.</p>
134
+
135
+ <mention-textarea
136
+ label="Tags"
137
+ supporting-text="Type # to add tags"
138
+ mention-trigger="#"
139
+ .suggestions="${this.suggestions.map((s) => ({ ...s, label: s.label.toLowerCase().replace(' ', '-') }))}"
140
+ ></mention-textarea>
141
+ </section>
142
+
143
+ <section class="demo-section">
144
+ <h2 class="display-large">Minimum Query Length</h2>
145
+ <p>This example requires at least 2 characters after @ before showing suggestions.</p>
146
+
147
+ <mention-textarea
148
+ label="Message with min query"
149
+ supporting-text="Type @+ at least 2 characters"
150
+ min-query-length="2"
151
+ .suggestions="${this.suggestions}"
152
+ ></mention-textarea>
153
+ </section>
154
+
155
+ <section class="demo-section">
156
+ <h2 class="display-large">Overflow: Hidden Test</h2>
157
+ <p>
158
+ This test ensures the popover works correctly even inside containers with overflow: hidden. The Popover API
159
+ should handle this properly.
160
+ </p>
161
+
162
+ <div
163
+ style="height: 150px; overflow: hidden; border: 2px solid var(--md-sys-color-outline); border-radius: 8px; padding: 16px;"
164
+ >
165
+ <p>Container with overflow: hidden (height: 150px)</p>
166
+ <mention-textarea
167
+ label="Test @mentions here"
168
+ supporting-text="Type @ - the suggestions should appear outside this container"
169
+ .suggestions="${this.suggestions}"
170
+ ></mention-textarea>
171
+ </div>
172
+
173
+ <div
174
+ style="height: 100px; width: 300px; overflow: hidden; border: 2px solid var(--md-sys-color-outline); border-radius: 8px; padding: 16px; margin-top: 20px;"
175
+ >
176
+ <p>Small container with overflow: hidden</p>
177
+ <mention-textarea label="Constrained" .suggestions="${this.suggestions}"></mention-textarea>
178
+ </div>
179
+ </section>
180
+
181
+ <style>
182
+ .demo-section {
183
+ max-width: 900px;
184
+ margin: 40px auto;
185
+ padding: 0 20px;
186
+ }
187
+
188
+ mention-textarea {
189
+ width: 100%;
190
+ margin-bottom: 20px;
191
+ }
192
+
193
+ pre {
194
+ font-family: 'Courier New', monospace;
195
+ font-size: 14px;
196
+ white-space: pre-wrap;
197
+ word-break: break-all;
198
+ }
199
+ </style>
200
+ `
201
+ }
202
+ }
203
+
204
+ const instance = new ComponentDemoPage()
205
+ instance.render()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -222,7 +222,7 @@
222
222
  "oauth2-mock-server": "^8.0.0",
223
223
  "prettier": "^3.5.1",
224
224
  "sinon": "^21.0.0",
225
- "ts-lit-plugin": "^2.0.2",
225
+ "@jarrodek/ts-lit-plugin": "^3.0.0",
226
226
  "typescript": "^5.5.2",
227
227
  "typescript-eslint": "^8.24.1",
228
228
  "wireit": "^0.14.4"
@@ -0,0 +1,274 @@
1
+ import { css } from 'lit'
2
+
3
+ export default css`
4
+ :host {
5
+ display: inline-flex;
6
+ flex-direction: column;
7
+ vertical-align: top;
8
+ outline: none;
9
+ -webkit-tap-highlight-color: transparent;
10
+ cursor: text;
11
+ min-width: 200px;
12
+ font-family: var(--md-sys-typescale-body-large-font);
13
+ font-size: var(--md-sys-typescale-body-large-size);
14
+ letter-spacing: var(--md-sys-typescale-body-large-tracking);
15
+ line-height: var(--md-sys-typescale-body-large-height);
16
+ }
17
+
18
+ .surface {
19
+ height: auto;
20
+ min-height: 56px;
21
+ position: relative;
22
+ display: flex;
23
+ align-items: flex-start;
24
+ cursor: inherit;
25
+ border-radius: var(--md-sys-shape-corner-extra-small);
26
+ }
27
+
28
+ .container {
29
+ position: absolute;
30
+ inset: 0;
31
+ z-index: 1;
32
+ border-radius: inherit;
33
+ background-color: transparent;
34
+ border: 1px solid var(--md-sys-color-outline);
35
+ outline: 0px solid var(--md-sys-color-primary);
36
+ outline-offset: -1px;
37
+ transition:
38
+ border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
39
+ outline-width 150ms cubic-bezier(0.4, 0, 0.2, 1),
40
+ outline-offset 150ms cubic-bezier(0.4, 0, 0.2, 1);
41
+ }
42
+
43
+ .content {
44
+ height: inherit;
45
+ min-height: inherit;
46
+ display: flex;
47
+ align-items: flex-start;
48
+ justify-content: start;
49
+ box-sizing: border-box;
50
+ position: relative;
51
+ z-index: 2;
52
+ flex: 1;
53
+ cursor: inherit;
54
+ padding: 16px;
55
+ }
56
+
57
+ .body {
58
+ flex: 1;
59
+ box-sizing: border-box;
60
+ position: relative;
61
+ cursor: inherit;
62
+ min-height: 24px;
63
+ }
64
+
65
+ .label {
66
+ color: var(--md-sys-color-on-surface-variant);
67
+ position: absolute;
68
+ top: 0px;
69
+ left: 0px;
70
+ max-width: calc(100% - 32px);
71
+ pointer-events: none;
72
+ overflow: hidden;
73
+ text-overflow: ellipsis;
74
+ white-space: nowrap;
75
+ z-index: 3;
76
+ font-family: var(--md-sys-typescale-body-large-font);
77
+ font-weight: var(--md-sys-typescale-body-large-weight);
78
+ font-size: var(--md-sys-typescale-body-large-size);
79
+ letter-spacing: var(--md-sys-typescale-body-large-tracking);
80
+ line-height: var(--md-sys-typescale-body-large-height);
81
+ transform-origin: left center;
82
+ transition:
83
+ transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
84
+ color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
85
+ background-color: var(--md-sys-color-surface);
86
+ padding: 0 4px;
87
+ margin-left: -4px;
88
+ }
89
+
90
+ .label.floating {
91
+ transform: translateY(-28px) scale(0.75);
92
+ color: var(--md-sys-color-primary);
93
+ }
94
+
95
+ .editor {
96
+ width: 100%;
97
+ min-height: 24px;
98
+ background: transparent;
99
+ border: none;
100
+ outline: none;
101
+ padding: 0;
102
+ margin: 0;
103
+ color: var(--md-sys-color-on-surface);
104
+ font-family: inherit;
105
+ font-size: inherit;
106
+ letter-spacing: inherit;
107
+ line-height: inherit;
108
+ cursor: inherit;
109
+ word-wrap: break-word;
110
+ overflow-wrap: break-word;
111
+ white-space: pre-wrap;
112
+ resize: none;
113
+ }
114
+
115
+ .editor:empty::before {
116
+ content: attr(data-placeholder);
117
+ color: var(--md-sys-color-on-surface-variant);
118
+ opacity: 0.6;
119
+ }
120
+
121
+ .mention-chip {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ margin: 0 2px;
125
+ vertical-align: baseline;
126
+ user-select: none;
127
+ }
128
+
129
+ .mention-chip ui-chip {
130
+ margin: 0;
131
+ font-size: inherit;
132
+ line-height: inherit;
133
+ }
134
+
135
+ .suggestions-popover {
136
+ position: fixed;
137
+ border: none;
138
+ margin: 0;
139
+ padding: 0;
140
+ background: var(--md-sys-color-surface-container);
141
+ box-shadow: var(--md-sys-elevation-2);
142
+ border-radius: var(--md-sys-shape-corner-medium);
143
+ max-height: 300px;
144
+ overflow-y: auto;
145
+ min-width: 200px;
146
+ max-width: 400px;
147
+ /* Popover API handles z-index and layering automatically */
148
+ }
149
+
150
+ .suggestion-item {
151
+ display: flex;
152
+ align-items: center;
153
+ padding: 12px 16px;
154
+ cursor: pointer;
155
+ border: none;
156
+ background: none;
157
+ width: 100%;
158
+ text-align: left;
159
+ font-family: inherit;
160
+ color: var(--md-sys-color-on-surface);
161
+ transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
162
+ }
163
+
164
+ .suggestion-item:hover {
165
+ background-color: var(--md-sys-color-surface-container-highest);
166
+ }
167
+
168
+ .suggestion-item.selected {
169
+ background-color: var(--md-sys-color-secondary-container);
170
+ color: var(--md-sys-color-on-secondary-container);
171
+ }
172
+
173
+ .suggestion-content {
174
+ flex: 1;
175
+ min-width: 0;
176
+ }
177
+
178
+ .suggestion-headline {
179
+ font-weight: var(--md-sys-typescale-label-large-weight);
180
+ font-size: var(--md-sys-typescale-label-large-size);
181
+ line-height: var(--md-sys-typescale-label-large-height);
182
+ margin-bottom: 2px;
183
+ }
184
+
185
+ .suggestion-supporting-text {
186
+ font-weight: var(--md-sys-typescale-body-small-weight);
187
+ font-size: var(--md-sys-typescale-body-small-size);
188
+ line-height: var(--md-sys-typescale-body-small-height);
189
+ color: var(--md-sys-color-on-surface-variant);
190
+ overflow: hidden;
191
+ text-overflow: ellipsis;
192
+ white-space: nowrap;
193
+ }
194
+
195
+ .suggestion-suffix {
196
+ margin-left: 8px;
197
+ font-weight: var(--md-sys-typescale-body-small-weight);
198
+ font-size: var(--md-sys-typescale-body-small-size);
199
+ line-height: var(--md-sys-typescale-body-small-height);
200
+ color: var(--md-sys-color-on-surface-variant);
201
+ flex-shrink: 0;
202
+ }
203
+
204
+ .supporting-text {
205
+ margin-top: 4px;
206
+ padding: 0 16px;
207
+ color: var(--md-sys-color-on-surface-variant);
208
+ font-family: var(--md-sys-typescale-body-small-font);
209
+ font-weight: var(--md-sys-typescale-body-small-weight);
210
+ font-size: var(--md-sys-typescale-body-small-size);
211
+ letter-spacing: var(--md-sys-typescale-body-small-tracking);
212
+ line-height: var(--md-sys-typescale-body-small-height);
213
+ }
214
+
215
+ /* Hover state */
216
+ :host(:hover:not(:focus-within):not([disabled])) .container {
217
+ border-color: var(--md-sys-color-on-surface);
218
+ }
219
+
220
+ /* Focus states */
221
+ :host(:focus-within) .container {
222
+ border-color: var(--md-sys-color-primary);
223
+ outline-width: 2px;
224
+ outline-offset: -2px;
225
+ }
226
+
227
+ :host(:focus-within) .label {
228
+ color: var(--md-sys-color-primary);
229
+ }
230
+
231
+ /* Disabled state */
232
+ :host([disabled]) {
233
+ pointer-events: none;
234
+ opacity: 0.38;
235
+ }
236
+
237
+ :host([disabled]) .container {
238
+ border-color: var(--md-sys-color-on-surface);
239
+ opacity: 0.12;
240
+ }
241
+
242
+ :host([disabled]) .label,
243
+ :host([disabled]) .editor {
244
+ color: var(--md-sys-color-on-surface);
245
+ opacity: 0.38;
246
+ }
247
+
248
+ /* Invalid state */
249
+ :host([invalid]) .container {
250
+ border-color: var(--md-sys-color-error);
251
+ }
252
+
253
+ :host([invalid]:focus-within) .container {
254
+ outline-color: var(--md-sys-color-error);
255
+ }
256
+
257
+ :host([invalid]) .label,
258
+ :host([invalid]:focus-within) .label {
259
+ color: var(--md-sys-color-error);
260
+ }
261
+
262
+ :host([invalid]) .supporting-text {
263
+ color: var(--md-sys-color-error);
264
+ }
265
+
266
+ /* Empty state for label floating */
267
+ .label:not(.floating) {
268
+ opacity: 1;
269
+ }
270
+
271
+ .has-content .label:not(.floating) {
272
+ opacity: 0;
273
+ }
274
+ `