uni-textarea-field 1.0.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/index.html ADDED
@@ -0,0 +1,456 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <title>Web Component: &lt;uni-textarea-field /> - a web component based on uniopen design language</title>
6
+ <meta name="description" content="uni-textarea-field is an encapsulated Web Component built upon the foundation of the uniopen design language. Implementation is straightforward: simply slot a standard textarea element inside uni-textarea-field. The component instantly applies a user interface that aligns seamlessly with the uniopen design language guidelines. Furthermore, its visual styles can be dynamically adapted via native HTML attributes or JavaScript properties. The component also exposes comprehensive character count and textarea length metadata, providing users with a clear and intuitive understanding of predefined character constraints." />
7
+ <script type="module" src="mjs/wc-uni-textarea-field.js"></script>
8
+ <link rel="stylesheet" href="https://blog.lalacube.com/mei/css/wc-base.css">
9
+ <link rel="stylesheet" href="https://blog.lalacube.com/mei/css/layers/radio-set.css">
10
+ <link rel="stylesheet" href="https://blog.lalacube.com/mei/css/layers/defaults.css">
11
+ <style>
12
+ #hd,#ft{display:none;}
13
+ body{position:relative;inline-size:100vw;block-size:100vh;margin:0;}
14
+
15
+ .wrap {
16
+ inline-size: 100%;
17
+ max-inline-size: min(800px, calc(100% - 2em));
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ .demo-wrap {
22
+ position: relative;
23
+ inline-size: 100%;
24
+ background-color: rgba(var(--white));
25
+ margin: 6em auto 1em;
26
+ padding: 1.5em;
27
+ box-sizing: border-box;
28
+ border-radius: 1em;
29
+ display: flex;
30
+ align-items: center;
31
+
32
+ &+.setting-form {
33
+ --subject-inline-size: auto;
34
+ margin-block-start: 1em;
35
+ }
36
+
37
+ uni-textarea-field {
38
+ inline-size: 100%;
39
+ }
40
+ }
41
+
42
+ uni-textarea-field {
43
+ inline-size: 300px;
44
+
45
+ &::part(icon-subject) {
46
+ /*background-color: red;*/
47
+ }
48
+
49
+ &[data-hide-icon]::part(icon-subject) {
50
+ display: none;
51
+ }
52
+ }
53
+
54
+ [inert] uni-textarea-field {
55
+ --interactivity: inert;
56
+ }
57
+ </style>
58
+ </head>
59
+
60
+ <body class="flex-center">
61
+ <div class="wrap">
62
+ <div class="demo-wrap">
63
+ <uni-textarea-field subject="Subject" message="Supporting text">
64
+ <textarea
65
+ slot="textarea"
66
+ placeholder="placeholder"
67
+ required
68
+ maxlength="500"
69
+ ></textarea>
70
+ </uni-textarea-field>
71
+ </div>
72
+
73
+ <form class="setting-form">
74
+ <ul class="adjustments">
75
+ <li class="adjustments__row">
76
+ <p class="adjustments__row__subject">size:</p>
77
+ <div class="adjustments__row__content">
78
+ <div class="il-pair a11y-block-link">
79
+ <div class="radio-set">
80
+ <input id="size-0" type="radio" name="size" value="large" />
81
+ <label class="radio-set__label" for="size-0"></label>
82
+ </div>
83
+
84
+ <label for="size-0">
85
+ large
86
+ </label>
87
+ </div>
88
+
89
+ <div class="il-pair a11y-block-link">
90
+ <div class="radio-set">
91
+ <input id="size-1" type="radio" name="size" value="medium" checked />
92
+ <label class="radio-set__label" for="size-1"></label>
93
+ </div>
94
+
95
+ <label for="size-1">
96
+ medium
97
+ </label>
98
+ </div>
99
+
100
+ <div class="il-pair a11y-block-link">
101
+ <div class="radio-set">
102
+ <input id="size-2" type="radio" name="size" value="small" />
103
+ <label class="radio-set__label" for="size-2"></label>
104
+ </div>
105
+
106
+ <label for="size-2">
107
+ small
108
+ </label>
109
+ </div>
110
+ </div>
111
+ </li>
112
+
113
+
114
+
115
+ <li class="adjustments__row">
116
+ <p class="adjustments__row__subject">appearance:</p>
117
+ <div class="adjustments__row__content">
118
+ <div class="il-pair a11y-block-link">
119
+ <div class="radio-set">
120
+ <input id="appearance-0" type="radio" name="appearance" value="filled" checked />
121
+ <label class="radio-set__label" for="appearance-0"></label>
122
+ </div>
123
+
124
+ <label for="appearance-0">
125
+ filled
126
+ </label>
127
+ </div>
128
+
129
+ <div class="il-pair a11y-block-link">
130
+ <div class="radio-set">
131
+ <input id="appearance-1" type="radio" name="appearance" value="outlined" />
132
+ <label class="radio-set__label" for="appearance-1"></label>
133
+ </div>
134
+
135
+ <label for="appearance-1">
136
+ outlined
137
+ </label>
138
+ </div>
139
+ </div>
140
+ </li>
141
+
142
+ <li class="adjustments__row">
143
+ <p class="adjustments__row__subject">stat:</p>
144
+ <div class="adjustments__row__content">
145
+ <div class="il-pair a11y-block-link">
146
+ <div class="radio-set">
147
+ <input id="stat-0" type="radio" name="stat" value="normal" checked />
148
+ <label class="radio-set__label" for="stat-0"></label>
149
+ </div>
150
+
151
+ <label for="stat-0">
152
+ normal
153
+ </label>
154
+ </div>
155
+
156
+ <div class="il-pair a11y-block-link">
157
+ <div class="radio-set">
158
+ <input id="stat-1" type="radio" name="stat" value="valid" />
159
+ <label class="radio-set__label" for="stat-1"></label>
160
+ </div>
161
+
162
+ <label for="stat-1">
163
+ valid
164
+ </label>
165
+ </div>
166
+
167
+ <div class="il-pair a11y-block-link">
168
+ <div class="radio-set">
169
+ <input id="stat-2" type="radio" name="stat" value="invalid" />
170
+ <label class="radio-set__label" for="stat-2"></label>
171
+ </div>
172
+
173
+ <label for="stat-2">
174
+ invalid
175
+ </label>
176
+ </div>
177
+
178
+ </div>
179
+ </li>
180
+
181
+ <li class="adjustments__row">
182
+ <p class="adjustments__row__subject">subject:</p>
183
+ <div class="adjustments__row__content">
184
+ <div class="il-pair a11y-block-link">
185
+ <div class="radio-set">
186
+ <input id="subject-0" type="radio" name="subject" value="show" checked />
187
+ <label class="radio-set__label" for="subject-0"></label>
188
+ </div>
189
+
190
+ <label for="subject-0">
191
+ show
192
+ </label>
193
+ </div>
194
+
195
+ <div class="il-pair a11y-block-link">
196
+ <div class="radio-set">
197
+ <input id="subject-1" type="radio" name="subject" value="hide" />
198
+ <label class="radio-set__label" for="subject-1"></label>
199
+ </div>
200
+
201
+ <label for="subject-1">
202
+ hide
203
+ </label>
204
+ </div>
205
+ </div>
206
+ </li>
207
+
208
+ <li class="adjustments__row">
209
+ <p class="adjustments__row__subject">subject icon:</p>
210
+ <div class="adjustments__row__content">
211
+ <div class="il-pair a11y-block-link">
212
+ <div class="radio-set">
213
+ <input id="subject-icon-0" type="radio" name="icon" value="show" checked />
214
+ <label class="radio-set__label" for="subject-icon-0"></label>
215
+ </div>
216
+
217
+ <label for="subject-icon-0">
218
+ show
219
+ </label>
220
+ </div>
221
+
222
+ <div class="il-pair a11y-block-link">
223
+ <div class="radio-set">
224
+ <input id="subject-icon-1" type="radio" name="icon" value="hide" />
225
+ <label class="radio-set__label" for="subject-icon-1"></label>
226
+ </div>
227
+
228
+ <label for="subject-icon-1">
229
+ hide
230
+ </label>
231
+ </div>
232
+ </div>
233
+ </li>
234
+
235
+ <li class="adjustments__row">
236
+ <p class="adjustments__row__subject">message:</p>
237
+ <div class="adjustments__row__content">
238
+ <div class="il-pair a11y-block-link">
239
+ <div class="radio-set">
240
+ <input id="message-0" type="radio" name="message" value="show" checked />
241
+ <label class="radio-set__label" for="message-0"></label>
242
+ </div>
243
+
244
+ <label for="message-0">
245
+ show
246
+ </label>
247
+ </div>
248
+
249
+ <div class="il-pair a11y-block-link">
250
+ <div class="radio-set">
251
+ <input id="message-1" type="radio" name="message" value="hide" />
252
+ <label class="radio-set__label" for="message-1"></label>
253
+ </div>
254
+
255
+ <label for="message-1">
256
+ hide
257
+ </label>
258
+ </div>
259
+ </div>
260
+ </li>
261
+
262
+ <li class="adjustments__row">
263
+ <p class="adjustments__row__subject">counter:</p>
264
+ <div class="adjustments__row__content">
265
+ <div class="il-pair a11y-block-link">
266
+ <div class="radio-set">
267
+ <input id="counter-0" type="radio" name="counter" value="show" checked />
268
+ <label class="radio-set__label" for="counter-0"></label>
269
+ </div>
270
+
271
+ <label for="counter-0">
272
+ show
273
+ </label>
274
+ </div>
275
+
276
+ <div class="il-pair a11y-block-link">
277
+ <div class="radio-set">
278
+ <input id="counter-1" type="radio" name="counter" value="hide" />
279
+ <label class="radio-set__label" for="counter-1"></label>
280
+ </div>
281
+
282
+ <label for="counter-1">
283
+ hide
284
+ </label>
285
+ </div>
286
+ </div>
287
+ </li>
288
+
289
+ <li class="adjustments__row">
290
+ <p class="adjustments__row__subject">required:</p>
291
+ <div class="adjustments__row__content">
292
+ <div class="il-pair a11y-block-link">
293
+ <div class="radio-set">
294
+ <input id="required-0" type="radio" name="required" value="y" checked />
295
+ <label class="radio-set__label" for="required-0"></label>
296
+ </div>
297
+
298
+ <label for="required-0">
299
+ yes
300
+ </label>
301
+ </div>
302
+
303
+ <div class="il-pair a11y-block-link">
304
+ <div class="radio-set">
305
+ <input id="required-1" type="radio" name="required" value="n" />
306
+ <label class="radio-set__label" for="required-1"></label>
307
+ </div>
308
+
309
+ <label for="required-1">
310
+ no
311
+ </label>
312
+ </div>
313
+ </div>
314
+ </li>
315
+
316
+ <li class="adjustments__row">
317
+ <p class="adjustments__row__subject">disabled:</p>
318
+ <div class="adjustments__row__content">
319
+ <div class="il-pair a11y-block-link">
320
+ <div class="radio-set">
321
+ <input id="disabled-0" type="radio" name="disabled" value="y" />
322
+ <label class="radio-set__label" for="disabled-0"></label>
323
+ </div>
324
+
325
+ <label for="disabled-0">
326
+ yes
327
+ </label>
328
+ </div>
329
+
330
+ <div class="il-pair a11y-block-link">
331
+ <div class="radio-set">
332
+ <input id="disabled-1" type="radio" name="disabled" value="n" checked />
333
+ <label class="radio-set__label" for="disabled-1"></label>
334
+ </div>
335
+
336
+ <label for="disabled-1">
337
+ no
338
+ </label>
339
+ </div>
340
+ </div>
341
+ </li>
342
+
343
+ <li class="adjustments__row">
344
+ <p class="adjustments__row__subject">readonly:</p>
345
+ <div class="adjustments__row__content">
346
+ <div class="il-pair a11y-block-link">
347
+ <div class="radio-set">
348
+ <input id="readonly-0" type="radio" name="readonly" value="y" />
349
+ <label class="radio-set__label" for="readonly-0"></label>
350
+ </div>
351
+
352
+ <label for="readonly-0">
353
+ yes
354
+ </label>
355
+ </div>
356
+
357
+ <div class="il-pair a11y-block-link">
358
+ <div class="radio-set">
359
+ <input id="readonly-1" type="radio" name="readonly" value="n" checked />
360
+ <label class="radio-set__label" for="readonly-1"></label>
361
+ </div>
362
+
363
+ <label for="readonly-1">
364
+ no
365
+ </label>
366
+ </div>
367
+ </div>
368
+ </li>
369
+ </ul>
370
+ </form>
371
+ </div>
372
+
373
+ <script type="module">
374
+ await customElements.whenDefined('uni-textarea-field');
375
+
376
+ const wrap = document.querySelector('.demo-wrap');
377
+ const uniTextareaField = document.querySelector('.demo-wrap uni-textarea-field');
378
+ const textarea = uniTextareaField.querySelector('textarea');
379
+ const form = document.querySelector('.setting-form');
380
+
381
+ const handler = (evt) => {
382
+ const { target } = evt;
383
+ const formData = new FormData(form);
384
+ const fd = Object.fromEntries(formData.entries());
385
+
386
+ switch (target.name) {
387
+ case 'size': {
388
+ uniTextareaField.size = fd['size'];
389
+ break;
390
+ }
391
+
392
+ case 'appearance': {
393
+ uniTextareaField.appearance = fd['appearance'];
394
+ break;
395
+ }
396
+
397
+ case 'stat': {
398
+ uniTextareaField.stat = fd['stat'] === 'normal' ? '' : fd['stat'];
399
+ break;
400
+ }
401
+
402
+ case 'subject': {
403
+ uniTextareaField.subject = fd['subject'] === 'show' ? 'Subject' : '';
404
+ break;
405
+ }
406
+
407
+ case 'message': {
408
+ uniTextareaField.message = fd['message'] === 'show' ? 'Supporting text' : '';
409
+ break;
410
+ }
411
+
412
+ case 'counter': {
413
+ if (fd['counter'] === 'show') {
414
+ textarea.maxLength = 30;
415
+ } else {
416
+ textarea.removeAttribute('maxlength');
417
+ }
418
+
419
+ uniTextareaField.refresh();
420
+ break;
421
+ }
422
+
423
+ case 'required': {
424
+ textarea.required = fd['required'] === 'y';
425
+
426
+ uniTextareaField.refresh();
427
+ break;
428
+ }
429
+
430
+ case 'disabled': {
431
+ textarea.disabled = fd['disabled'] === 'y';
432
+
433
+ uniTextareaField.refresh();
434
+ break;
435
+ }
436
+
437
+ case 'readonly': {
438
+ textarea.readOnly = fd['readonly'] === 'y';
439
+
440
+ uniTextareaField.refresh();
441
+ break;
442
+ }
443
+
444
+ case 'icon': {
445
+ uniTextareaField.toggleAttribute('data-hide-icon', fd['icon'] === 'hide')
446
+ break;
447
+ }
448
+ }
449
+ };
450
+
451
+ form.addEventListener('input', handler);
452
+ </script>
453
+
454
+ </body>
455
+
456
+ </html>
@@ -0,0 +1,171 @@
1
+ export const _wccss = `
2
+ /* reset */
3
+ div,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,legend,input,textarea,p,article,aside,figcaption,figure,nav,section,mark,audio,video,main{margin:0;padding:0}
4
+ article,aside,figcaption,figure,nav,section,main{display:block}
5
+ fieldset,img{border:0}
6
+ address,caption,cite,em,strong{font-style:normal;font-weight:400}
7
+ ol,ul{list-style:none}
8
+ caption{text-align:left}
9
+ h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}
10
+ abbr{border:0;font-variant:normal}
11
+ input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}
12
+ body{-webkit-text-size-adjust:none}
13
+ select,input,button,textarea{font:100% arial,helvetica,clean,sans-serif;}
14
+ del{font-style:normal;text-decoration:none}
15
+ pre{font-family:monospace;line-height:100%}
16
+ progress{-webkit-appearance:none;appearance:none;overflow:hidden;border:0 none;}
17
+
18
+ /* component style */
19
+ a{cursor:pointer;text-decoration:none;}
20
+ .stuff{text-indent:100%;white-space:nowrap;overflow:hidden;}
21
+ .aspect-ratio{position:relative;width:100%;--w:4;--h:3;}
22
+ .aspect-ratio:before{content:'';width:100%;padding-top:calc(var(--h) * 100% / var(--w));display:block;}
23
+ .aspect-ratio .content{position:absolute;top:0;left:0;right:0;bottom:0;}
24
+ .text-overflow{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
25
+ .line-clampin{display:-webkit-box;-webkit-line-clamp:var(--line-clamp, 2);-webkit-box-orient:vertical;text-overflow:ellipsis;overflow:hidden;}
26
+ .overscrolling{-webkit-overflow-scrolling:touch;overflow:hidden;overflow-y:scroll;overscroll-behavior:contain;}
27
+ .overscrolling-x{-webkit-overflow-scrolling:touch;overflow:hidden;overflow-x:scroll;overscroll-behavior:contain;}
28
+ .absolute-center{position:absolute;top:0;left:0;bottom:0;right:0;margin:auto;}
29
+ .flex-center{display:flex;justify-content:center;align-items:center;}
30
+ .force-radius{overflow:hidden;transform:translate3d(0, 0, 0);border-radius:var(--r, 8px);}
31
+ .pretty-paragraph{word-break:break-word;hyphens:auto;text-wrap:pretty;white-space:pre-wrap;}
32
+ .fade-in-out {
33
+ --min-block-size: var(--fade-in-out-min-block-size, 100px);
34
+ --max-block-size: var(--fade-in-out-max-block-size, 500px);
35
+ --gap: var(--fade-in-out-gap, 16px);
36
+
37
+ --mask-vertical-size: var(--gap);
38
+ --mask-vertical: linear-gradient(
39
+ to bottom,
40
+ transparent 0%,
41
+ black calc(0% + var(--mask-vertical-size)),
42
+ black calc(100% - var(--mask-vertical-size)),
43
+ transparent 100%
44
+ );
45
+
46
+ /* scroll */
47
+ --scrollbar-inline-size: 2px;
48
+ --scrollbar-block-size: 2px;
49
+ --scrollbar-background: transparent;
50
+ --scrollbar-thumb: var(--fade-in-out-scrollbar-thumb-color, rgba(0 0 0/.2));
51
+
52
+ inline-size: 100%;
53
+ min-block-size: var(--min-block-size);
54
+ max-block-size: var(--max-block-size);
55
+ overflow: hidden;
56
+ overflow-y: auto;
57
+ -webkit-overflow-scrolling: touch;
58
+ overscroll-behavior: contain;
59
+ box-sizing: border-box;
60
+ mask-image: var(--mask-vertical);
61
+ -webkit-mask-image: var(--mask-vertical);
62
+ padding-block: var(--gap);
63
+
64
+ &::-webkit-scrollbar {
65
+ inline-size: var(--scrollbar-inline-size);
66
+ block-size: var(--scrollbar-block-size);
67
+ }
68
+
69
+ &::-webkit-scrollbar-track {
70
+ background: var(--scrollbar-background);
71
+ }
72
+
73
+ &::-webkit-scrollbar-thumb {
74
+ border-radius: var(--scrollbar-block-size);
75
+ background: var(--scrollbar-thumb);
76
+ }
77
+ }
78
+ .button-two-face {
79
+ --button-size: 40;
80
+ --button-size-with-unit: calc(var(--button-size) * 1px);
81
+ --button-icon-scale-rate: .75;
82
+
83
+ --button-background: rgba(202 230 252);
84
+ --button-icon: rgba(8 28 53);
85
+ --button-box-shadow: none;
86
+ --button-active-scale: .8;
87
+
88
+ --button-icon-scale-basis: calc((var(--button-size) * var(--button-icon-scale-rate)) / 24);
89
+ --before-icon: none;
90
+ --before-scale: var(--button-icon-scale-basis);
91
+ --after-icon: none;
92
+ --after-scale: 0;
93
+
94
+ flex-shrink: 0;
95
+ font-size: 0;
96
+ appearance: none;
97
+ box-shadow: unset;
98
+ border: unset;
99
+ background: transparent;
100
+ -webkit-user-select: none;
101
+ user-select: none;
102
+ pointer-events: auto;
103
+ margin: 0;
104
+ padding: 0;
105
+ outline: 0 none;
106
+
107
+ position: relative;
108
+ inline-size: var(--button-size-with-unit);
109
+ aspect-ratio: 1/1;
110
+ border-radius: var(--button-size-with-unit);
111
+ background: var(--button-background);
112
+ box-shadow: var(--button-box-shadow);
113
+
114
+ transition: background .2s ease;
115
+ will-change: background;
116
+
117
+ &:active {
118
+ scale: var(--button-active-scale);
119
+ }
120
+
121
+ &::before,
122
+ &::after {
123
+ position: absolute;
124
+ inset-inline-start: 50%;
125
+ inset-block-start: 50%;
126
+ content: '';
127
+ inline-size: 24px;
128
+ aspect-ratio: 1/1;
129
+ background: var(--button-icon);
130
+ margin-inline-start: -12px;
131
+ margin-block-start: -12px;
132
+ transition: scale 250ms ease;
133
+ will-change: scale;
134
+ pointer-events: none;
135
+ }
136
+
137
+ &::before {
138
+ scale: var(--before-scale);
139
+ clip-path: var(--before-icon);
140
+ }
141
+
142
+ &::after {
143
+ scale: var(--after-scale);
144
+ clip-path: var(--after-icon);
145
+ }
146
+
147
+ &[data-reverse] {
148
+ --before-scale: 0;
149
+ --after-scale: var(--button-icon-scale-basis);
150
+ }
151
+ }
152
+
153
+ :host{all:initial;font-family:system-ui,sans-serif;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-size:16px;-webkit-tap-highlight-color:transparent;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
154
+ :host {
155
+ /**
156
+ * safe area variables for iX design
157
+ */
158
+ --safe-area-left: 0px;
159
+ --safe-area-right: 0px;
160
+ --safe-area-top: 0px;
161
+ --safe-area-bottom: 0px;
162
+ }
163
+ @supports (bottom: env(safe-area-inset-top)) {
164
+ :host {
165
+ --safe-area-left: env(safe-area-inset-left);
166
+ --safe-area-right: env(safe-area-inset-right);
167
+ --safe-area-top: env(safe-area-inset-top);
168
+ --safe-area-bottom: env(safe-area-inset-bottom);
169
+ }
170
+ }
171
+ `;