@solid-design-system/mcp 7.0.3 → 7.1.0
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/CHANGELOG.md +2 -0
- package/metadata/packages/components/sd-datepicker/info.md +5 -0
- package/metadata/packages/components/sd-datepicker/stories/clearable.md +7 -0
- package/metadata/packages/icons/default/system.json +10 -10
- package/metadata/packages/templates/file-selector.md +266 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,7 @@ Use the components tool by passing the args `component` and `example` for any of
|
|
|
16
16
|
- component: sd-datepicker, example: disabled
|
|
17
17
|
- component: sd-datepicker, example: visually-disabled
|
|
18
18
|
- component: sd-datepicker, example: help-text
|
|
19
|
+
- component: sd-datepicker, example: clearable
|
|
19
20
|
- component: sd-datepicker, example: required
|
|
20
21
|
- component: sd-datepicker, example: valid
|
|
21
22
|
- component: sd-datepicker, example: invalid
|
|
@@ -56,6 +57,7 @@ Use the components tool by passing the args `component` and `example` for any of
|
|
|
56
57
|
- prop.visuallyDisabled [attr: visually-disabled]: boolean, default=false — Makes the control non-interactive visually (like disabled) without disabling it functionally.
|
|
57
58
|
- prop.styleOnValid [attr: style-on-valid]: boolean, default=false — When true, applies success styling for valid selections.
|
|
58
59
|
- prop.readonly: boolean, default=false — Makes the input read-only (non-editable) when true.
|
|
60
|
+
- prop.clearable: boolean, default=false — Adds a clear button when the datepicker is not empty.
|
|
59
61
|
- prop.placement: 'top'|'bottom', default='bottom' — Preferred placement of the flyout relative to the input (top|bottom).
|
|
60
62
|
- prop.placeholder: string, default='' — Placeholder text for the input when no date is selected.
|
|
61
63
|
- prop.name: string, default='' — The name of the datepicker, submitted as a name/value pair with form data.
|
|
@@ -79,6 +81,7 @@ Use the components tool by passing the args `component` and `example` for any of
|
|
|
79
81
|
- event.sd-blur: Emitted when the datepicker is blurred.
|
|
80
82
|
- event.sd-month-change: Emitted when the month is changed.
|
|
81
83
|
- event.sd-month-year: Emitted when the year is changed.
|
|
84
|
+
- event.sd-clear: Emitted when the clear button is activated.
|
|
82
85
|
|
|
83
86
|
### Slots
|
|
84
87
|
|
|
@@ -86,6 +89,7 @@ Use the components tool by passing the args `component` and `example` for any of
|
|
|
86
89
|
- slot.label: The label for the date input.
|
|
87
90
|
- slot.help-text: The help text, displayed below the input.
|
|
88
91
|
- slot.tooltip: The tooltip icon and content.
|
|
92
|
+
- slot.clear-icon: An icon to use in lieu of the default clear icon.
|
|
89
93
|
|
|
90
94
|
### CSS Parts
|
|
91
95
|
|
|
@@ -106,6 +110,7 @@ Use the components tool by passing the args `component` and `example` for any of
|
|
|
106
110
|
- part.invalid-icon: The icon shown when the input is invalid.
|
|
107
111
|
- part.valid-icon: The icon shown when the input is valid.
|
|
108
112
|
- part.form-control-help-text: The help text, displayed below the input.
|
|
113
|
+
- part.clear-button: The clear button.
|
|
109
114
|
|
|
110
115
|
## Guidelines
|
|
111
116
|
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"displayNameDe": "Sonne",
|
|
30
30
|
"tags": ["sonnig", "hell", "schönes Wetter"]
|
|
31
31
|
},
|
|
32
|
-
{
|
|
33
|
-
"technicalId": "settings",
|
|
34
|
-
"displayNameDe": "Einstellungen",
|
|
35
|
-
"tags": ["anpassen", "Bearbeitung", "Zahnrad"]
|
|
36
|
-
},
|
|
37
32
|
{
|
|
38
33
|
"technicalId": "volume",
|
|
39
34
|
"displayNameDe": "Lautstärke",
|
|
40
35
|
"tags": ["Ton", "Pegel", "Geräuschpegel"]
|
|
41
36
|
},
|
|
37
|
+
{
|
|
38
|
+
"technicalId": "settings",
|
|
39
|
+
"displayNameDe": "Einstellungen",
|
|
40
|
+
"tags": ["anpassen", "Bearbeitung", "Zahnrad"]
|
|
41
|
+
},
|
|
42
42
|
{
|
|
43
43
|
"technicalId": "hand",
|
|
44
44
|
"displayNameDe": "Hand",
|
|
@@ -844,16 +844,16 @@
|
|
|
844
844
|
"displayNameDe": "Lesezeichen",
|
|
845
845
|
"tags": ["merken", "Markierung", "markiert", "Favoriten"]
|
|
846
846
|
},
|
|
847
|
-
{
|
|
848
|
-
"technicalId": "flash",
|
|
849
|
-
"displayNameDe": "Blitz",
|
|
850
|
-
"tags": ["Pfeil", "Fehler", "falsch"]
|
|
851
|
-
},
|
|
852
847
|
{
|
|
853
848
|
"technicalId": "arrow-right",
|
|
854
849
|
"displayNameDe": "Pfeil rechts",
|
|
855
850
|
"tags": ["Pfeil", "vorwärts", "weiter"]
|
|
856
851
|
},
|
|
852
|
+
{
|
|
853
|
+
"technicalId": "flash",
|
|
854
|
+
"displayNameDe": "Blitz",
|
|
855
|
+
"tags": ["Pfeil", "Fehler", "falsch"]
|
|
856
|
+
},
|
|
857
857
|
{
|
|
858
858
|
"technicalId": "arrow-top",
|
|
859
859
|
"displayNameDe": "Pfeil oben",
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: file-selector
|
|
3
|
+
title: File Selector
|
|
4
|
+
components:
|
|
5
|
+
- sd-button
|
|
6
|
+
- sd-divider
|
|
7
|
+
- sd-file-selector
|
|
8
|
+
- sd-headline
|
|
9
|
+
- sd-headline--size-3xl
|
|
10
|
+
- sd-headline--size-4xl
|
|
11
|
+
- sd-icon
|
|
12
|
+
- sd-input
|
|
13
|
+
- sd-interactive
|
|
14
|
+
- sd-interactive--reset
|
|
15
|
+
- sd-meta
|
|
16
|
+
- sd-meta--light
|
|
17
|
+
- sd-meta--size-sm
|
|
18
|
+
- sd-option
|
|
19
|
+
- sd-paragraph
|
|
20
|
+
- sd-progress-bar
|
|
21
|
+
- sd-select
|
|
22
|
+
- sd-tooltip
|
|
23
|
+
version: 1.0.0
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Template: File Selector with Default variant
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<section>
|
|
30
|
+
<form id="identity-form" class="flex flex-col gap-6">
|
|
31
|
+
<div class="flex flex-col gap-2">
|
|
32
|
+
<h3 class="sd-headline sd-headline--size-4xl">Confirm your identity</h3>
|
|
33
|
+
<p class="sd-paragraph">
|
|
34
|
+
To safeguard your account, we need to verify who you are. Choose a document type below and upload a clear scan.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="flex flex-col gap-6">
|
|
38
|
+
<span class="sd-meta sd-meta--size-sm sd-meta--light">Fields marked with * are required.</span>
|
|
39
|
+
<sd-select name="documentType" label="Document type" placeholder="Please select" required>
|
|
40
|
+
<sd-option value="passport">Passport</sd-option>
|
|
41
|
+
<sd-option value="id-card">ID card</sd-option>
|
|
42
|
+
</sd-select>
|
|
43
|
+
<sd-file-selector name="identityDocument" label="Upload document" show-label required></sd-file-selector>
|
|
44
|
+
</div>
|
|
45
|
+
</form>
|
|
46
|
+
</section>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Template: File Selector with Drop Area variant
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<section>
|
|
53
|
+
<form id="droparea-documents-form" class="flex flex-col gap-6">
|
|
54
|
+
<h3 class="sd-headline sd-headline--size-3xl">Tax Documents & Depot Statements</h3>
|
|
55
|
+
<p class="sd-paragraph">
|
|
56
|
+
Please share your tax and depot records for the current year. Digital documents help us provide you with accurate
|
|
57
|
+
tax reporting services.
|
|
58
|
+
</p>
|
|
59
|
+
<div class="flex flex-col">
|
|
60
|
+
<span class="sd-meta sd-meta--size-sm sd-meta--light">Fields marked with * are required.</span>
|
|
61
|
+
<sd-file-selector
|
|
62
|
+
id="droparea-file-selector"
|
|
63
|
+
name="droparea-file-selector"
|
|
64
|
+
label="Upload documents"
|
|
65
|
+
show-label
|
|
66
|
+
drop-area
|
|
67
|
+
multiple
|
|
68
|
+
required
|
|
69
|
+
accept=".pdf,.jpg,.jpeg,.png"
|
|
70
|
+
help-text="Maximum file size 3 MB. Supported: PDF, JPG, PNG."
|
|
71
|
+
class="mt-6"
|
|
72
|
+
></sd-file-selector>
|
|
73
|
+
<ul id="droparea-file-list" class="file-list flex flex-col gap-2 mt-2" aria-live="polite"></ul>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="flex flex-col gap-4 md:flex-row md:justify-end">
|
|
76
|
+
<sd-button variant="secondary" onclick="alert('Cancelled process')">Cancel</sd-button>
|
|
77
|
+
<sd-tooltip id="droparea-submit-tooltip" trigger="hover focus" content="Please select one or more files.">
|
|
78
|
+
<sd-button id="droparea-submit-button" type="submit" visually-disabled>Submit documents</sd-button>
|
|
79
|
+
</sd-tooltip>
|
|
80
|
+
</div>
|
|
81
|
+
</form>
|
|
82
|
+
</section>
|
|
83
|
+
<script type="module">
|
|
84
|
+
await Promise.all([
|
|
85
|
+
customElements.whenDefined('sd-file-selector'),
|
|
86
|
+
customElements.whenDefined('sd-progress-bar'),
|
|
87
|
+
customElements.whenDefined('sd-button'),
|
|
88
|
+
customElements.whenDefined('sd-icon'),
|
|
89
|
+
customElements.whenDefined('sd-divider'),
|
|
90
|
+
customElements.whenDefined('sd-tooltip')
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const MAX_FILE_SIZE = 3 * 1024 * 1024;
|
|
94
|
+
const ALLOWED_FILE_TYPES = ['application/pdf', 'image/jpeg', 'image/png'];
|
|
95
|
+
const NO_FILES_MESSAGE = 'Please select one or more files.';
|
|
96
|
+
const INVALID_FILES_MESSAGE = 'One or more files have errors. Please remove them and try again.';
|
|
97
|
+
const fileSelector = document.getElementById('droparea-file-selector');
|
|
98
|
+
const fileList = document.getElementById('droparea-file-list');
|
|
99
|
+
const submitButton = document.getElementById('droparea-submit-button');
|
|
100
|
+
const submitTooltip = document.getElementById('droparea-submit-tooltip');
|
|
101
|
+
let files = [];
|
|
102
|
+
|
|
103
|
+
// preview-ignore:start
|
|
104
|
+
const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
105
|
+
const simulateUpload = async (file, progressBar, fileName) => {
|
|
106
|
+
for (let progress = 0; progress <= 100; progress += 1) {
|
|
107
|
+
progressBar.value = progress;
|
|
108
|
+
progressBar.label = 'Uploading ' + file.name + ': ' + progress + '%';
|
|
109
|
+
|
|
110
|
+
await delay(50);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
progressBar.label = file.name + ' uploaded successfully';
|
|
114
|
+
await delay(300);
|
|
115
|
+
|
|
116
|
+
progressBar.remove();
|
|
117
|
+
fileName.classList.remove('shrink-0', 'text-neutral-700');
|
|
118
|
+
fileName.classList.add('flex-1', 'text-black');
|
|
119
|
+
};
|
|
120
|
+
// preview-ignore:end
|
|
121
|
+
|
|
122
|
+
const updateSubmitButtonState = () => {
|
|
123
|
+
const hasFiles = files.length > 0;
|
|
124
|
+
const hasInvalidFiles = fileList?.querySelector('li.invalid') !== null;
|
|
125
|
+
const isDisabled = !hasFiles || hasInvalidFiles;
|
|
126
|
+
|
|
127
|
+
submitButton.visuallyDisabled = isDisabled;
|
|
128
|
+
submitTooltip.disabled = !isDisabled;
|
|
129
|
+
submitTooltip.content = hasInvalidFiles ? INVALID_FILES_MESSAGE : NO_FILES_MESSAGE;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
fileSelector.addEventListener('sd-input', event => {
|
|
133
|
+
fileList?.replaceChildren();
|
|
134
|
+
|
|
135
|
+
files = Array.from(event.currentTarget.files ?? []);
|
|
136
|
+
fileList?.classList.toggle('hidden', files.length === 0);
|
|
137
|
+
|
|
138
|
+
files.forEach((file, index) => {
|
|
139
|
+
const listItem = document.createElement('li');
|
|
140
|
+
const listRow = document.createElement('div');
|
|
141
|
+
const fileName = document.createElement('span');
|
|
142
|
+
const removeButton = document.createElement('button');
|
|
143
|
+
|
|
144
|
+
listItem.classList.add('flex', 'flex-col', 'text-sm', 'gap-2', 'text-left');
|
|
145
|
+
listRow.classList.add('flex', 'items-center', 'gap-2', 'w-full');
|
|
146
|
+
fileName.classList.add('shrink-0', 'truncate', 'text-neutral-700');
|
|
147
|
+
fileName.textContent = file.name;
|
|
148
|
+
|
|
149
|
+
removeButton.type = 'button';
|
|
150
|
+
removeButton.classList.add('sd-interactive', 'sd-interactive--reset', 'shrink-0', 'flex', 'items-center');
|
|
151
|
+
removeButton.title = 'Remove ' + file.name;
|
|
152
|
+
removeButton.setAttribute('aria-label', 'Remove ' + file.name);
|
|
153
|
+
|
|
154
|
+
const trashIcon = document.createElement('sd-icon');
|
|
155
|
+
trashIcon.setAttribute('name', 'system/trash');
|
|
156
|
+
trashIcon.setAttribute('color', 'currentColor');
|
|
157
|
+
trashIcon.setAttribute('aria-hidden', 'true');
|
|
158
|
+
trashIcon.classList.add('w-4', 'h-4');
|
|
159
|
+
|
|
160
|
+
removeButton.append(trashIcon);
|
|
161
|
+
listRow.append(fileName);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Client-side validation example.
|
|
165
|
+
* Files must also be validated by the server.
|
|
166
|
+
*/
|
|
167
|
+
const errors = [];
|
|
168
|
+
|
|
169
|
+
if (!ALLOWED_FILE_TYPES.includes(file.type)) errors.push('Unsupported file type');
|
|
170
|
+
if (file.size > MAX_FILE_SIZE) errors.push('The file is too large. Maximum allowed size is 3 MB.');
|
|
171
|
+
|
|
172
|
+
if (errors.length === 0) {
|
|
173
|
+
const progressBar = document.createElement('sd-progress-bar');
|
|
174
|
+
|
|
175
|
+
progressBar.classList.add('flex-1');
|
|
176
|
+
progressBar.max = 100;
|
|
177
|
+
progressBar.value = 0;
|
|
178
|
+
progressBar.label = 'Uploading ' + file.name + ': 0%';
|
|
179
|
+
|
|
180
|
+
listRow.append(progressBar);
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Connect the progress bar to your application's
|
|
184
|
+
* upload progress here.
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
// preview-ignore:start
|
|
188
|
+
simulateUpload(file, progressBar, fileName);
|
|
189
|
+
// preview-ignore:end
|
|
190
|
+
} else {
|
|
191
|
+
fileName.classList.add('flex-1');
|
|
192
|
+
}
|
|
193
|
+
listRow.append(removeButton);
|
|
194
|
+
listItem.append(listRow);
|
|
195
|
+
|
|
196
|
+
errors.forEach(message => {
|
|
197
|
+
const errorMessage = document.createElement('div');
|
|
198
|
+
|
|
199
|
+
errorMessage.classList.add('flex', 'items-center', 'gap-2', 'text-error', 'text-left', 'text-sm');
|
|
200
|
+
fileName.classList.remove('text-neutral-700');
|
|
201
|
+
fileName.classList.add('text-black');
|
|
202
|
+
errorMessage.setAttribute('role', 'alert');
|
|
203
|
+
|
|
204
|
+
const errorIcon = document.createElement('sd-icon');
|
|
205
|
+
errorIcon.setAttribute('name', 'system/warning');
|
|
206
|
+
errorIcon.setAttribute('color', 'currentColor');
|
|
207
|
+
errorIcon.setAttribute('aria-hidden', 'true');
|
|
208
|
+
|
|
209
|
+
const errorText = document.createElement('span');
|
|
210
|
+
errorText.textContent = message;
|
|
211
|
+
|
|
212
|
+
errorMessage.append(errorIcon, errorText);
|
|
213
|
+
listItem.append(errorMessage);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
removeButton.addEventListener('click', () => {
|
|
217
|
+
files = files.filter(remainingFile => remainingFile !== file);
|
|
218
|
+
|
|
219
|
+
const dataTransfer = new DataTransfer();
|
|
220
|
+
|
|
221
|
+
files.forEach(remainingFile => {
|
|
222
|
+
dataTransfer.items.add(remainingFile);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
fileSelector.files = dataTransfer.files;
|
|
226
|
+
listItem.remove();
|
|
227
|
+
|
|
228
|
+
const lastListItem = fileList?.querySelector('li:last-child');
|
|
229
|
+
const lastDivider = lastListItem?.querySelector(':scope > sd-divider');
|
|
230
|
+
|
|
231
|
+
lastDivider?.remove();
|
|
232
|
+
fileList?.classList.toggle('hidden', files.length === 0);
|
|
233
|
+
updateSubmitButtonState();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (errors.length > 0) listItem.classList.add('invalid');
|
|
237
|
+
|
|
238
|
+
if (index < files.length - 1) {
|
|
239
|
+
const divider = document.createElement('sd-divider');
|
|
240
|
+
divider.setAttribute('aria-hidden', 'true');
|
|
241
|
+
listItem.append(divider);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
fileList?.append(listItem);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
updateSubmitButtonState();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const form = document.getElementById('droparea-documents-form');
|
|
251
|
+
form.onsubmit = event => {
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
|
|
254
|
+
if (submitButton.visuallyDisabled) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (form.checkValidity()) {
|
|
259
|
+
alert('Drop area form submitted');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
form.reportValidity();
|
|
264
|
+
};
|
|
265
|
+
</script>
|
|
266
|
+
```
|
package/package.json
CHANGED
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"prettier": "^3.9.5",
|
|
21
21
|
"tsx": "^4.23.1",
|
|
22
22
|
"typescript": "^5.9.3",
|
|
23
|
-
"@solid-design-system/components": "7.0
|
|
24
|
-
"@solid-design-system/styles": "7.0
|
|
25
|
-
"@solid-design-system/tokens": "7.0
|
|
23
|
+
"@solid-design-system/components": "7.1.0",
|
|
24
|
+
"@solid-design-system/styles": "7.1.0",
|
|
25
|
+
"@solid-design-system/tokens": "7.1.0"
|
|
26
26
|
},
|
|
27
27
|
"exports": {
|
|
28
28
|
".": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"directory": "packages/mcp"
|
|
60
60
|
},
|
|
61
61
|
"type": "module",
|
|
62
|
-
"version": "7.0
|
|
62
|
+
"version": "7.1.0",
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "pnpm run build:components && pnpm run build:tokens && pnpm run build:ts && pnpm run build:metadata && cd ../.. && pnpm exec prettier --write --log-level warn packages/mcp/metadata",
|
|
65
65
|
"build:components": "cd ../components && pnpm exec node scripts/make-metadata.js --outdir dist",
|