@threadlabs/looma 0.11.4 → 0.11.6
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/components/ui-button/ui-button.html +17 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-button.css +22 -0
- package/vue/UiButton.vue +19 -0
- package/vue/chunks/UiButton.js +1 -1
- package/vue/components.css +52 -34
package/package.json
CHANGED
package/styles/ui-button.css
CHANGED
|
@@ -222,6 +222,23 @@
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
226
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
227
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
228
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
229
|
+
html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"] {
|
|
230
|
+
position: relative;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"]::after {
|
|
234
|
+
position: absolute;
|
|
235
|
+
inset: 50% auto auto 50%;
|
|
236
|
+
inline-size: max(100%, var(--ui-control-min-block-size));
|
|
237
|
+
block-size: max(100%, var(--ui-control-min-block-size));
|
|
238
|
+
transform: translate(-50%, -50%);
|
|
239
|
+
content: "";
|
|
240
|
+
}
|
|
241
|
+
|
|
225
242
|
/* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
|
|
226
243
|
WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
|
|
227
244
|
:scope[data-ui-button-state~="variant=link"] {
|
|
@@ -302,6 +319,11 @@
|
|
|
302
319
|
@media (pointer: coarse) {
|
|
303
320
|
}
|
|
304
321
|
|
|
322
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
323
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
324
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
325
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
326
|
+
|
|
305
327
|
/* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
|
|
306
328
|
WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
|
|
307
329
|
|
package/vue/UiButton.vue
CHANGED
|
@@ -299,6 +299,25 @@ function attribute(value: unknown): any {
|
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
303
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
304
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
305
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
306
|
+
html[data-ui-input-modality="touch"]
|
|
307
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"] {
|
|
308
|
+
position: relative;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
html[data-ui-input-modality="touch"]
|
|
312
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"]::after {
|
|
313
|
+
position: absolute;
|
|
314
|
+
inset: 50% auto auto 50%;
|
|
315
|
+
inline-size: max(100%, var(--ui-control-min-block-size));
|
|
316
|
+
block-size: max(100%, var(--ui-control-min-block-size));
|
|
317
|
+
transform: translate(-50%, -50%);
|
|
318
|
+
content: "";
|
|
319
|
+
}
|
|
320
|
+
|
|
302
321
|
/* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
|
|
303
322
|
WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
|
|
304
323
|
[data-component~="ui-button"][data-ui-button-state~="variant=link"] {
|
package/vue/chunks/UiButton.js
CHANGED
package/vue/components.css
CHANGED
|
@@ -368,7 +368,7 @@ ol[data-v-0d86d58e] {
|
|
|
368
368
|
display: none;
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
-
[data-component~="ui-button"][data-v-
|
|
371
|
+
[data-component~="ui-button"][data-v-97872575] {
|
|
372
372
|
--_hue: var(--ui-accent);
|
|
373
373
|
--_hue-hover: var(--ui-accent-hover);
|
|
374
374
|
--_hue-active: var(--ui-accent-active);
|
|
@@ -387,15 +387,15 @@ ol[data-v-0d86d58e] {
|
|
|
387
387
|
vertical-align: middle;
|
|
388
388
|
color: var(--_tone-text);
|
|
389
389
|
}
|
|
390
|
-
[data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-
|
|
390
|
+
[data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-97872575] {
|
|
391
391
|
justify-content: flex-start;
|
|
392
392
|
text-align: start;
|
|
393
393
|
}
|
|
394
|
-
[data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-
|
|
394
|
+
[data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-97872575] {
|
|
395
395
|
display: flex;
|
|
396
396
|
inline-size: 100%;
|
|
397
397
|
}
|
|
398
|
-
[data-component~="ui-button"][data-v-
|
|
398
|
+
[data-component~="ui-button"][data-v-97872575] {
|
|
399
399
|
appearance: none;
|
|
400
400
|
min-block-size: var(--ui-button-min-block-size, var(--ui-control-size-md));
|
|
401
401
|
border-style: solid;
|
|
@@ -420,17 +420,17 @@ ol[data-v-0d86d58e] {
|
|
|
420
420
|
margin: 0;
|
|
421
421
|
}
|
|
422
422
|
@supports (text-box-trim: trim-both) {
|
|
423
|
-
[data-component~="ui-button"][data-v-
|
|
423
|
+
[data-component~="ui-button"][data-v-97872575] {
|
|
424
424
|
text-box-trim: trim-both;
|
|
425
425
|
text-box-edge: cap alphabetic;
|
|
426
426
|
}
|
|
427
427
|
}
|
|
428
|
-
[data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-
|
|
428
|
+
[data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-97872575] {
|
|
429
429
|
min-block-size: var(--ui-control-size-sm);
|
|
430
430
|
padding: var(--ui-space-1) var(--ui-space-2);
|
|
431
431
|
font-size: var(--ui-font-size-sm);
|
|
432
432
|
}
|
|
433
|
-
[data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-
|
|
433
|
+
[data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-97872575] {
|
|
434
434
|
min-block-size: var(--ui-control-size-lg);
|
|
435
435
|
padding: var(--ui-space-3) var(--ui-space-4);
|
|
436
436
|
font-size: var(--ui-font-size-md);
|
|
@@ -438,29 +438,29 @@ ol[data-v-0d86d58e] {
|
|
|
438
438
|
|
|
439
439
|
/* A link is never :enabled, so interactive states test "not unavailable" instead: for a button that
|
|
440
440
|
is exactly :enabled, at the same specificity, and it also matches a link that can be followed. */
|
|
441
|
-
[data-component~="ui-button"][data-v-
|
|
441
|
+
[data-component~="ui-button"][data-v-97872575]:hover:not(:disabled, [aria-disabled="true"]) {
|
|
442
442
|
border-color: var(--ui-button-hover-border, var(--_tone-hover));
|
|
443
443
|
background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
/* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
|
|
447
|
-
[data-component~="ui-button"][data-v-
|
|
447
|
+
[data-component~="ui-button"][data-v-97872575]:active:not(:disabled, [aria-disabled="true"]) {
|
|
448
448
|
background-color: var(--ui-button-active-surface, oklch(from var(--_tone) l c h / 18%));
|
|
449
449
|
background-image: none;
|
|
450
450
|
box-shadow: var(--ui-pressed);
|
|
451
451
|
}
|
|
452
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-
|
|
453
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-
|
|
452
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-97872575],
|
|
453
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-97872575] {
|
|
454
454
|
border-color: var(--_tone);
|
|
455
455
|
background-color: var(--_tone);
|
|
456
456
|
color: var(--_on-tone);
|
|
457
457
|
box-shadow: var(--ui-button-solid-shadow, var(--ui-raised));
|
|
458
458
|
}
|
|
459
|
-
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-
|
|
459
|
+
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-97872575], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-97872575]):hover:not(:disabled, [aria-disabled="true"]) {
|
|
460
460
|
background-color: var(--_tone-hover);
|
|
461
461
|
border-color: var(--_tone-hover);
|
|
462
462
|
}
|
|
463
|
-
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-
|
|
463
|
+
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-97872575], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-97872575]):active:not(:disabled, [aria-disabled="true"]) {
|
|
464
464
|
background-color: var(--_tone-active);
|
|
465
465
|
border-color: var(--_tone-active);
|
|
466
466
|
background-image: none;
|
|
@@ -469,43 +469,43 @@ ol[data-v-0d86d58e] {
|
|
|
469
469
|
|
|
470
470
|
/* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
|
|
471
471
|
destructive secondary action, not a special case. */
|
|
472
|
-
[data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-
|
|
472
|
+
[data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-97872575] {
|
|
473
473
|
--_hue: var(--ui-text);
|
|
474
474
|
--_hue-hover: var(--ui-text);
|
|
475
475
|
--_hue-active: var(--ui-text);
|
|
476
476
|
--_hue-text: var(--ui-text);
|
|
477
477
|
--_on-hue: var(--ui-surface);
|
|
478
478
|
}
|
|
479
|
-
[data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-
|
|
480
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-
|
|
479
|
+
[data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-97872575],
|
|
480
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-97872575] {
|
|
481
481
|
--_hue: var(--ui-danger);
|
|
482
482
|
--_hue-hover: var(--ui-danger-hover);
|
|
483
483
|
--_hue-active: var(--ui-danger-hover);
|
|
484
484
|
--_hue-text: var(--ui-danger-hover);
|
|
485
485
|
--_on-hue: var(--ui-on-danger);
|
|
486
486
|
}
|
|
487
|
-
[data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-
|
|
487
|
+
[data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-97872575] {
|
|
488
488
|
--_hue: var(--ui-success);
|
|
489
489
|
--_hue-hover: color-mix(in srgb, var(--ui-success) 85%, #000);
|
|
490
490
|
--_hue-active: color-mix(in srgb, var(--ui-success) 75%, #000);
|
|
491
491
|
--_hue-text: color-mix(in srgb, var(--ui-success) 85%, #000);
|
|
492
492
|
--_on-hue: var(--ui-surface);
|
|
493
493
|
}
|
|
494
|
-
[data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-
|
|
494
|
+
[data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-97872575] {
|
|
495
495
|
--_hue: var(--ui-warning);
|
|
496
496
|
--_hue-hover: color-mix(in srgb, var(--ui-warning) 85%, #000);
|
|
497
497
|
--_hue-active: color-mix(in srgb, var(--ui-warning) 75%, #000);
|
|
498
498
|
--_hue-text: color-mix(in srgb, var(--ui-warning) 85%, #000);
|
|
499
499
|
--_on-hue: var(--ui-surface);
|
|
500
500
|
}
|
|
501
|
-
[data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-
|
|
501
|
+
[data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-97872575] {
|
|
502
502
|
--_hue: var(--ui-info);
|
|
503
503
|
--_hue-hover: color-mix(in srgb, var(--ui-info) 85%, #000);
|
|
504
504
|
--_hue-active: color-mix(in srgb, var(--ui-info) 75%, #000);
|
|
505
505
|
--_hue-text: color-mix(in srgb, var(--ui-info) 85%, #000);
|
|
506
506
|
--_on-hue: var(--ui-surface);
|
|
507
507
|
}
|
|
508
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-
|
|
508
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-97872575] {
|
|
509
509
|
border-color: transparent;
|
|
510
510
|
background: transparent;
|
|
511
511
|
box-shadow: none;
|
|
@@ -513,24 +513,24 @@ ol[data-v-0d86d58e] {
|
|
|
513
513
|
}
|
|
514
514
|
|
|
515
515
|
/* Tints derive from the text colour, not a surface token, so hover and press show on any background. */
|
|
516
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-
|
|
516
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-97872575]:hover:not(:disabled, [aria-disabled="true"]) {
|
|
517
517
|
background-color: var(--ui-button-ghost-hover-surface, oklch(from var(--_tone) l c h / 10%));
|
|
518
518
|
border-color: transparent;
|
|
519
519
|
}
|
|
520
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-
|
|
520
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-97872575]:active:not(:disabled, [aria-disabled="true"]) {
|
|
521
521
|
background-color: var(--ui-button-ghost-active-surface, oklch(from var(--_tone) l c h / 16%));
|
|
522
522
|
border-color: transparent;
|
|
523
523
|
box-shadow: var(--ui-pressed);
|
|
524
524
|
}
|
|
525
525
|
|
|
526
526
|
/* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
|
|
527
|
-
[data-component~="ui-button"][data-v-
|
|
527
|
+
[data-component~="ui-button"][data-v-97872575]:focus-visible {
|
|
528
528
|
outline: none;
|
|
529
529
|
box-shadow:
|
|
530
530
|
var(--ui-button-shadow, var(--ui-raised)),
|
|
531
531
|
var(--ui-focus-halo);
|
|
532
532
|
}
|
|
533
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-
|
|
533
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-97872575]:focus-visible {
|
|
534
534
|
box-shadow:
|
|
535
535
|
var(--ui-button-solid-shadow, var(--ui-raised)),
|
|
536
536
|
var(--ui-focus-halo);
|
|
@@ -540,7 +540,7 @@ ol[data-v-0d86d58e] {
|
|
|
540
540
|
disabled outline is still an outline and a disabled ghost still has no edge. Intent goes — an
|
|
541
541
|
unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
|
|
542
542
|
highlight and shadow, because nothing that cannot be pressed should look raised. */
|
|
543
|
-
[data-component~="ui-button"][data-v-
|
|
543
|
+
[data-component~="ui-button"][data-v-97872575]:is(:disabled, [aria-disabled="true"]) {
|
|
544
544
|
/* Derived from the tone itself, not from a separate disabled palette: faded toward the
|
|
545
545
|
page, which desaturates it at the same time. Toward the page rather than toward white,
|
|
546
546
|
so a dark theme dims where a light one lightens — the same rule, both schemes. */
|
|
@@ -557,7 +557,7 @@ ol[data-v-0d86d58e] {
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
/* A filled button keeps its fill, faded further so its text stays legible on it. */
|
|
560
|
-
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-
|
|
560
|
+
:is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-97872575], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-97872575]):is(:disabled, [aria-disabled="true"]) {
|
|
561
561
|
--_tone: color-mix(in oklab, var(--_hue) 34%, var(--ui-surface));
|
|
562
562
|
}
|
|
563
563
|
|
|
@@ -565,19 +565,37 @@ ol[data-v-0d86d58e] {
|
|
|
565
565
|
it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
|
|
566
566
|
the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
|
|
567
567
|
hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
|
|
568
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-
|
|
568
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-97872575]:is(:disabled, [aria-disabled="true"]) {
|
|
569
569
|
background-color: oklch(from var(--_tone) l c h / 14%);
|
|
570
570
|
border-color: transparent;
|
|
571
571
|
}
|
|
572
572
|
@media (pointer: coarse) {
|
|
573
|
-
[data-component~="ui-button"][data-v-
|
|
573
|
+
[data-component~="ui-button"][data-v-97872575] {
|
|
574
574
|
min-block-size: var(--ui-control-min-block-size);
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
+
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
579
|
+
area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
|
|
580
|
+
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
581
|
+
area. Only the link needs it: a hit area on every button would reach over its neighbours. */
|
|
582
|
+
html[data-ui-input-modality="touch"]
|
|
583
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575] {
|
|
584
|
+
position: relative;
|
|
585
|
+
}
|
|
586
|
+
html[data-ui-input-modality="touch"]
|
|
587
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575]::after {
|
|
588
|
+
position: absolute;
|
|
589
|
+
inset: 50% auto auto 50%;
|
|
590
|
+
inline-size: max(100%, var(--ui-control-min-block-size));
|
|
591
|
+
block-size: max(100%, var(--ui-control-min-block-size));
|
|
592
|
+
transform: translate(-50%, -50%);
|
|
593
|
+
content: "";
|
|
594
|
+
}
|
|
595
|
+
|
|
578
596
|
/* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
|
|
579
597
|
WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
|
|
580
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-
|
|
598
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575] {
|
|
581
599
|
display: inline;
|
|
582
600
|
min-block-size: 0;
|
|
583
601
|
padding: 0;
|
|
@@ -595,22 +613,22 @@ ol[data-v-0d86d58e] {
|
|
|
595
613
|
color var(--ui-motion-fast) var(--ui-motion-ease),
|
|
596
614
|
text-decoration-color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
597
615
|
}
|
|
598
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-
|
|
616
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575]:hover:not(:disabled, [aria-disabled="true"]) {
|
|
599
617
|
color: var(--ui-button-link-hover-text, var(--ui-text-primary));
|
|
600
618
|
text-decoration-color: currentColor;
|
|
601
619
|
background: none;
|
|
602
620
|
transform: none;
|
|
603
621
|
}
|
|
604
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-
|
|
622
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575]:active:not(:disabled, [aria-disabled="true"]) {
|
|
605
623
|
background: none;
|
|
606
624
|
transform: none;
|
|
607
625
|
}
|
|
608
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-
|
|
626
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575]:is(:disabled, [aria-disabled="true"]) {
|
|
609
627
|
background: none;
|
|
610
628
|
border-color: transparent;
|
|
611
629
|
color: var(--ui-button-disabled-text, var(--ui-disabled-text));
|
|
612
630
|
}
|
|
613
|
-
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-
|
|
631
|
+
[data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-97872575]:focus-visible {
|
|
614
632
|
box-shadow: none;
|
|
615
633
|
/* Fallback keeps the ring if the token is missing: an unresolved var() would void the outline. */
|
|
616
634
|
outline: 2px solid var(--ui-focus-ring);
|
|
@@ -618,7 +636,7 @@ ol[data-v-0d86d58e] {
|
|
|
618
636
|
}
|
|
619
637
|
|
|
620
638
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
621
|
-
[data-component~="ui-button"][hidden][data-v-
|
|
639
|
+
[data-component~="ui-button"][hidden][data-v-97872575] {
|
|
622
640
|
display: none;
|
|
623
641
|
}
|
|
624
642
|
|