@lessly/ui 3.0.0 → 4.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/dist/{chunk-CEXND5YW.js → chunk-LTHOLX7I.js} +223 -28
- package/dist/index.d.ts +359 -57
- package/dist/index.js +886 -752
- package/dist/router.d.ts +27 -17
- package/dist/router.js +72 -98
- package/dist/styles-federated.css +63 -10
- package/dist/styles.css +63 -16
- package/package.json +14 -14
|
@@ -71,10 +71,12 @@ var buttonVariants = cva(
|
|
|
71
71
|
// colour would reach the element beside the variant's and stylesheet order would pick.
|
|
72
72
|
variant: {
|
|
73
73
|
default: "bg-blue-400 text-text-on-brand focus-visible:ring-border-focus hover:bg-blue-350",
|
|
74
|
-
/** The
|
|
75
|
-
*
|
|
74
|
+
/** The wash carries the danger at rest, on the neutral frame every object button wears — so
|
|
75
|
+
* a pair still ranks by weight rather than by colour, and the signal survives a touch
|
|
76
|
+
* device that never hovers. The label rests neutral because a variant differs by frame and
|
|
77
|
+
* fill and never by text colour (#449). Hover reddens the frame and the label; keyboard
|
|
76
78
|
* focus reddens the ring alone. */
|
|
77
|
-
destructive: "border border-border-
|
|
79
|
+
destructive: "border border-border-default bg-bg-danger-subtle text-text-primary focus-visible:ring-border-danger hover:border-border-danger hover:text-text-danger",
|
|
78
80
|
/** The borderless form of `destructive`, for an icon-only revoke repeated down a list — a row
|
|
79
81
|
* of framed ✕ buttons would shout. */
|
|
80
82
|
"destructive-ghost": "text-text-tertiary focus-visible:ring-border-focus hover:bg-bg-danger-subtle hover:text-text-danger",
|
|
@@ -83,7 +85,7 @@ var buttonVariants = cva(
|
|
|
83
85
|
* `StepUpChallenge destructive` one. Use `destructive` for any trigger that sits on a page. */
|
|
84
86
|
"destructive-solid": "bg-bg-danger text-text-on-danger focus-visible:ring-border-focus hover:bg-bg-danger-a90",
|
|
85
87
|
outline: "border border-border-default bg-transparent text-text-primary focus-visible:ring-border-focus hover:bg-bg-secondary",
|
|
86
|
-
secondary: "bg-bg-secondary text-text-primary focus-visible:ring-border-focus hover:bg-bg-secondary-a80",
|
|
88
|
+
secondary: "border border-border-default bg-bg-secondary text-text-primary focus-visible:ring-border-focus hover:bg-bg-secondary-a80",
|
|
87
89
|
/** The frameless console tone, and the add affordance — `variant="ghost" size="xs"` with a
|
|
88
90
|
* leading `+`. It rests at `text-text-primary` because a button's variant differs by frame
|
|
89
91
|
* and fill, never by text colour; `destructive-ghost` keeps its quieter rest on purpose. */
|
|
@@ -290,7 +292,7 @@ var SheetContent = React3.forwardRef(({ side = "right", className, children, ...
|
|
|
290
292
|
...props,
|
|
291
293
|
children: [
|
|
292
294
|
children,
|
|
293
|
-
/* @__PURE__ */ jsxs2(SheetPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm text-text-tertiary opacity-70 transition-opacity duration-fast hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-border-focus disabled:pointer-events-none", children: [
|
|
295
|
+
/* @__PURE__ */ jsxs2(SheetPrimitive.Close, { className: "absolute right-4 top-4 cursor-pointer rounded-sm text-text-tertiary opacity-70 transition-opacity duration-fast hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-border-focus disabled:pointer-events-none", children: [
|
|
294
296
|
/* @__PURE__ */ jsx3(X, { className: "h-4 w-4" }),
|
|
295
297
|
/* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Close" })
|
|
296
298
|
] })
|
|
@@ -334,6 +336,112 @@ var SheetDescription = React3.forwardRef(({ className, ...props }, ref) => /* @_
|
|
|
334
336
|
));
|
|
335
337
|
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|
336
338
|
|
|
339
|
+
// src/components/scroll-area.tsx
|
|
340
|
+
import * as React4 from "react";
|
|
341
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
342
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
343
|
+
var ScrollArea = React4.forwardRef(({ className, children, viewportClassName, viewportProps, type = "scroll", scrollHideDelay = 600, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
344
|
+
ScrollAreaPrimitive.Root,
|
|
345
|
+
{
|
|
346
|
+
ref,
|
|
347
|
+
type,
|
|
348
|
+
scrollHideDelay,
|
|
349
|
+
className: cn("group/scroll relative overflow-hidden", className),
|
|
350
|
+
...props,
|
|
351
|
+
children: [
|
|
352
|
+
/* @__PURE__ */ jsx4(
|
|
353
|
+
ScrollAreaPrimitive.Viewport,
|
|
354
|
+
{
|
|
355
|
+
...viewportProps,
|
|
356
|
+
className: cn("scroll-overlay-viewport h-full w-full rounded-[inherit]", viewportClassName),
|
|
357
|
+
children
|
|
358
|
+
}
|
|
359
|
+
),
|
|
360
|
+
/* @__PURE__ */ jsx4(ScrollBar, {}),
|
|
361
|
+
/* @__PURE__ */ jsx4(ScrollAreaPrimitive.Corner, {})
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
));
|
|
365
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
366
|
+
var ScrollBar = React4.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
367
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
368
|
+
{
|
|
369
|
+
ref,
|
|
370
|
+
orientation,
|
|
371
|
+
forceMount: true,
|
|
372
|
+
className: cn(
|
|
373
|
+
"motion-scroll-overlay flex touch-none select-none p-[2px] opacity-0",
|
|
374
|
+
"data-[state=visible]:opacity-100 group-hover/scroll:opacity-100 hover:opacity-100",
|
|
375
|
+
orientation === "vertical" && "h-full w-2",
|
|
376
|
+
orientation === "horizontal" && "h-2 flex-col",
|
|
377
|
+
className
|
|
378
|
+
),
|
|
379
|
+
...props,
|
|
380
|
+
children: /* @__PURE__ */ jsx4(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border-strong" })
|
|
381
|
+
}
|
|
382
|
+
));
|
|
383
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
384
|
+
|
|
385
|
+
// src/components/entity-row.tsx
|
|
386
|
+
import * as React5 from "react";
|
|
387
|
+
import { Slot as Slot2 } from "@radix-ui/react-slot";
|
|
388
|
+
import { ChevronDown, ChevronRight } from "lucide-react";
|
|
389
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
390
|
+
var rowClass = "flex w-full items-center gap-3 px-4 py-2.5 text-left";
|
|
391
|
+
var plateClass = "relative isolate after:pointer-events-none after:absolute after:inset-x-1.5 after:inset-y-1 after:-z-10 after:rounded-lg hover:after:bg-[var(--hov-bg)] disabled:hover:after:bg-transparent focus-visible:outline-none focus-visible:after:ring-2 focus-visible:after:ring-inset focus-visible:after:ring-border-focus";
|
|
392
|
+
var stackClass = "flex-col items-stretch gap-1";
|
|
393
|
+
var caretMotion = {
|
|
394
|
+
transitionDuration: "var(--motion-duration-normal)",
|
|
395
|
+
transitionTimingFunction: "var(--motion-easing-standard)"
|
|
396
|
+
};
|
|
397
|
+
var EntityRow = React5.forwardRef(function EntityRow2({ mark, title, badge, sub, meta, summary, opens = "page", expanded, asChild = false, className, children, ...props }, ref) {
|
|
398
|
+
if (process.env.NODE_ENV !== "production" && opens === "disclosure" && expanded === void 0) {
|
|
399
|
+
console.warn(
|
|
400
|
+
'[EntityRow] `opens="disclosure"` needs `expanded`: it draws the caret AND sets `aria-expanded`, and without it the row renders a caret that never turns and states no state at all. Pass `expanded={open}`, or use `opens="page"` / `opens="modal"`.'
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
const line = /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
404
|
+
mark,
|
|
405
|
+
/* @__PURE__ */ jsxs4("span", { className: "min-w-0 flex-1", children: [
|
|
406
|
+
/* @__PURE__ */ jsxs4("span", { className: "flex min-h-5 items-center gap-2", children: [
|
|
407
|
+
/* @__PURE__ */ jsx5("span", { className: cn("truncate text-sm text-text-primary", opens !== "none" && "font-medium"), children: title }),
|
|
408
|
+
badge != null && /* @__PURE__ */ jsx5("span", { className: "flex h-5 shrink-0 items-center", children: badge })
|
|
409
|
+
] }),
|
|
410
|
+
sub != null && /* @__PURE__ */ jsx5("span", { className: "block truncate text-xs text-text-secondary", children: sub })
|
|
411
|
+
] }),
|
|
412
|
+
meta != null && /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-sm text-text-secondary", children: meta }),
|
|
413
|
+
opens !== "none" && /* @__PURE__ */ jsxs4("span", { className: "flex size-4 shrink-0 items-center justify-center", children: [
|
|
414
|
+
opens === "page" && /* @__PURE__ */ jsx5(ChevronRight, { className: "size-4 text-text-tertiary", "aria-hidden": true }),
|
|
415
|
+
opens === "disclosure" && /* @__PURE__ */ jsx5(
|
|
416
|
+
ChevronDown,
|
|
417
|
+
{
|
|
418
|
+
className: cn(
|
|
419
|
+
"size-4 shrink-0 text-text-tertiary transition-transform",
|
|
420
|
+
expanded ? "rotate-180" : "rotate-0"
|
|
421
|
+
),
|
|
422
|
+
style: caretMotion,
|
|
423
|
+
"aria-hidden": true
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
] })
|
|
427
|
+
] });
|
|
428
|
+
const content = summary == null ? line : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
429
|
+
/* @__PURE__ */ jsx5("span", { className: "flex w-full items-center gap-3", children: line }),
|
|
430
|
+
/* @__PURE__ */ jsx5("span", { className: "block w-full text-xs text-text-secondary", children: summary })
|
|
431
|
+
] });
|
|
432
|
+
const classes = cn(rowClass, opens !== "none" && ["cursor-pointer", plateClass], summary != null && stackClass, className);
|
|
433
|
+
const stateProps = opens === "disclosure" ? { "aria-expanded": expanded } : void 0;
|
|
434
|
+
if (opens === "none") {
|
|
435
|
+
return /* @__PURE__ */ jsx5("div", { ...props, ref, className: classes, children: content });
|
|
436
|
+
}
|
|
437
|
+
if (asChild) {
|
|
438
|
+
const child = React5.Children.only(children);
|
|
439
|
+
return /* @__PURE__ */ jsx5(Slot2, { ...props, ref, className: classes, ...stateProps, children: React5.cloneElement(child, stateProps, content) });
|
|
440
|
+
}
|
|
441
|
+
return /* @__PURE__ */ jsx5("button", { ...props, ref, type: "button", className: classes, ...stateProps, children: content });
|
|
442
|
+
});
|
|
443
|
+
EntityRow.displayName = "EntityRow";
|
|
444
|
+
|
|
337
445
|
// src/hooks/use-mobile.ts
|
|
338
446
|
import { useEffect, useState as useState2 } from "react";
|
|
339
447
|
var MD_BREAKPOINT = 768;
|
|
@@ -376,10 +484,11 @@ function useSidebar(options = {}) {
|
|
|
376
484
|
}
|
|
377
485
|
|
|
378
486
|
// src/components/nav-row.tsx
|
|
379
|
-
import * as
|
|
380
|
-
import { Slot as
|
|
381
|
-
import {
|
|
382
|
-
|
|
487
|
+
import * as React6 from "react";
|
|
488
|
+
import { Slot as Slot3 } from "@radix-ui/react-slot";
|
|
489
|
+
import { ChevronDown as ChevronDown2, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
490
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
491
|
+
var base = "flex w-full cursor-pointer items-center text-left text-sm transition-colors duration-fast focus-visible:outline-none";
|
|
383
492
|
var ring = "focus-visible:ring-2 focus-visible:ring-border-focus";
|
|
384
493
|
var plate = "relative isolate after:pointer-events-none after:absolute after:inset-x-1.5 after:inset-y-1 after:-z-10 after:rounded-lg";
|
|
385
494
|
var shape = {
|
|
@@ -390,23 +499,43 @@ var shape = {
|
|
|
390
499
|
var state = {
|
|
391
500
|
rail: {
|
|
392
501
|
idle: "font-medium text-text-secondary hover:bg-[var(--hov-bg)] hover:text-text-primary",
|
|
393
|
-
active: "bg-bg
|
|
502
|
+
active: "bg-[var(--current-bg)] font-semibold text-text-primary hover:bg-[var(--hov-current)]"
|
|
394
503
|
},
|
|
395
504
|
menu: {
|
|
396
505
|
idle: "font-medium text-text-secondary hover:bg-[var(--menu-hov)]",
|
|
397
|
-
active: "
|
|
506
|
+
active: "ring-1 ring-inset ring-border-strong font-semibold text-text-primary hover:bg-[var(--menu-hov)]"
|
|
398
507
|
},
|
|
399
508
|
// A settings line reads as a label, not as navigation, so it idles at the body weight the rest of
|
|
400
509
|
// the card is set in — and never marks itself current, because it isn't.
|
|
401
510
|
card: {
|
|
402
|
-
idle: "text-text-primary hover:after:bg-bg
|
|
403
|
-
active: "text-text-primary hover:after:bg-bg
|
|
511
|
+
idle: "text-text-primary hover:after:bg-[var(--hov-bg)]",
|
|
512
|
+
active: "text-text-primary hover:after:bg-[var(--hov-bg)]"
|
|
404
513
|
}
|
|
405
514
|
};
|
|
406
|
-
|
|
515
|
+
function Mark({ mark, open }) {
|
|
516
|
+
return /* @__PURE__ */ jsx6(
|
|
517
|
+
"span",
|
|
518
|
+
{
|
|
519
|
+
className: cn(
|
|
520
|
+
"flex size-5 items-center justify-center",
|
|
521
|
+
mark === "drill" ? "rounded-md transition-colors duration-fast group-hover/nav-row:bg-[color-mix(in_srgb,var(--text-primary)_10%,transparent)]" : "text-text-tertiary"
|
|
522
|
+
),
|
|
523
|
+
children: mark === "drill" ? /* @__PURE__ */ jsx6(ChevronRight2, { className: "size-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx6(
|
|
524
|
+
ChevronDown2,
|
|
525
|
+
{
|
|
526
|
+
className: "size-4",
|
|
527
|
+
style: { ...caretMotion, transitionProperty: "transform", transform: open ? "rotate(180deg)" : "rotate(0deg)" },
|
|
528
|
+
"aria-hidden": "true"
|
|
529
|
+
}
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
var NavRow = React6.forwardRef(function NavRow2({ icon, label, sub, active = false, variant, trailing, mark, markOpen, mutedTrailing = false, hideLabel = false, asChild = false, className, children, ...props }, ref) {
|
|
407
535
|
const card = variant === "card";
|
|
408
|
-
const
|
|
409
|
-
|
|
536
|
+
const adornment = mark ? !hideLabel && /* @__PURE__ */ jsx6(Mark, { mark, open: markOpen }) : trailing;
|
|
537
|
+
const content = /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
538
|
+
Boolean(icon) && /* @__PURE__ */ jsx6(
|
|
410
539
|
"span",
|
|
411
540
|
{
|
|
412
541
|
className: cn(
|
|
@@ -417,31 +546,92 @@ var NavRow = React4.forwardRef(function NavRow2({ icon, label, sub, active = fal
|
|
|
417
546
|
children: icon
|
|
418
547
|
}
|
|
419
548
|
),
|
|
420
|
-
!hideLabel && (card ? /* @__PURE__ */
|
|
421
|
-
/* @__PURE__ */
|
|
422
|
-
Boolean(sub) && /* @__PURE__ */
|
|
423
|
-
] }) : /* @__PURE__ */
|
|
424
|
-
Boolean(
|
|
549
|
+
!hideLabel && (card ? /* @__PURE__ */ jsxs5("span", { "data-slot": "nav-row-label", className: "min-w-0 flex-1", children: [
|
|
550
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate", children: label }),
|
|
551
|
+
Boolean(sub) && /* @__PURE__ */ jsx6("span", { className: "block truncate text-xs text-text-secondary", children: sub })
|
|
552
|
+
] }) : /* @__PURE__ */ jsx6("span", { "data-slot": "nav-row-label", className: "min-w-0 flex-1 truncate", children: label })),
|
|
553
|
+
Boolean(adornment) && /* @__PURE__ */ jsx6(
|
|
425
554
|
"span",
|
|
426
555
|
{
|
|
556
|
+
"data-slot": "nav-row-trailing",
|
|
427
557
|
className: cn(
|
|
428
558
|
"flex shrink-0 items-center",
|
|
429
559
|
mutedTrailing ? "text-text-tertiary" : card && "text-text-secondary"
|
|
430
560
|
),
|
|
431
|
-
children:
|
|
561
|
+
children: adornment
|
|
432
562
|
}
|
|
433
563
|
),
|
|
434
|
-
card && /* @__PURE__ */
|
|
564
|
+
card && /* @__PURE__ */ jsx6("span", { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
435
565
|
] });
|
|
436
|
-
const classes = cn(
|
|
566
|
+
const classes = cn(
|
|
567
|
+
base,
|
|
568
|
+
shape[variant],
|
|
569
|
+
state[variant][active ? "active" : "idle"],
|
|
570
|
+
// The drill mark's own plate lights on the row, so the row is what the plate hangs off.
|
|
571
|
+
mark === "drill" && "group/nav-row",
|
|
572
|
+
className
|
|
573
|
+
);
|
|
574
|
+
const expanded = mark === "disclosure" ? markOpen : void 0;
|
|
437
575
|
if (asChild) {
|
|
438
|
-
const child =
|
|
439
|
-
return /* @__PURE__ */
|
|
576
|
+
const child = React6.Children.only(children);
|
|
577
|
+
return /* @__PURE__ */ jsx6(Slot3, { ref, className: classes, "aria-label": hideLabel ? label : void 0, "aria-expanded": expanded, ...props, children: React6.cloneElement(child, void 0, content) });
|
|
440
578
|
}
|
|
441
|
-
return /* @__PURE__ */
|
|
579
|
+
return /* @__PURE__ */ jsx6("button", { ref, type: "button", className: classes, "aria-label": hideLabel ? label : void 0, "aria-expanded": expanded, ...props, children: content });
|
|
442
580
|
});
|
|
443
581
|
NavRow.displayName = "NavRow";
|
|
444
582
|
|
|
583
|
+
// src/components/rail-toggle.tsx
|
|
584
|
+
import * as React7 from "react";
|
|
585
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
586
|
+
var RAIL_COLUMN_BOX = "flex size-10 shrink-0 items-center justify-center [&>*]:shrink-0";
|
|
587
|
+
var BAR_TRAVEL = 6;
|
|
588
|
+
function RailMark({ collapsed, className }) {
|
|
589
|
+
return /* @__PURE__ */ jsxs6(
|
|
590
|
+
"svg",
|
|
591
|
+
{
|
|
592
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
593
|
+
viewBox: "0 0 24 24",
|
|
594
|
+
fill: "none",
|
|
595
|
+
stroke: "currentColor",
|
|
596
|
+
strokeWidth: 2,
|
|
597
|
+
strokeLinecap: "round",
|
|
598
|
+
strokeLinejoin: "round",
|
|
599
|
+
className: cn("size-4", className),
|
|
600
|
+
"aria-hidden": "true",
|
|
601
|
+
children: [
|
|
602
|
+
/* @__PURE__ */ jsx7("rect", { "data-slot": "rail-mark-frame", x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
603
|
+
/* @__PURE__ */ jsx7(
|
|
604
|
+
"path",
|
|
605
|
+
{
|
|
606
|
+
"data-slot": "rail-mark-bar",
|
|
607
|
+
className: "motion-rail-collapse",
|
|
608
|
+
d: "M9 3v18",
|
|
609
|
+
style: { transform: `translateX(${collapsed ? BAR_TRAVEL : 0}px)` }
|
|
610
|
+
}
|
|
611
|
+
)
|
|
612
|
+
]
|
|
613
|
+
}
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
var RailToggle = React7.forwardRef(function RailToggle2({ collapsed, className, "aria-label": ariaLabel, ...rest }, ref) {
|
|
617
|
+
return /* @__PURE__ */ jsx7(
|
|
618
|
+
"button",
|
|
619
|
+
{
|
|
620
|
+
ref,
|
|
621
|
+
type: "button",
|
|
622
|
+
"aria-label": ariaLabel ?? (collapsed ? "Expand sidebar" : "Collapse sidebar"),
|
|
623
|
+
"aria-expanded": !collapsed,
|
|
624
|
+
className: cn(
|
|
625
|
+
RAIL_COLUMN_BOX,
|
|
626
|
+
"cursor-pointer rounded-md text-text-secondary transition-colors duration-fast hover:bg-[var(--hov-bg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
627
|
+
className
|
|
628
|
+
),
|
|
629
|
+
...rest,
|
|
630
|
+
children: /* @__PURE__ */ jsx7(RailMark, { collapsed })
|
|
631
|
+
}
|
|
632
|
+
);
|
|
633
|
+
});
|
|
634
|
+
|
|
445
635
|
export {
|
|
446
636
|
cn,
|
|
447
637
|
buttonVariants,
|
|
@@ -460,7 +650,12 @@ export {
|
|
|
460
650
|
SheetFooter,
|
|
461
651
|
SheetTitle,
|
|
462
652
|
SheetDescription,
|
|
653
|
+
ScrollArea,
|
|
654
|
+
ScrollBar,
|
|
655
|
+
EntityRow,
|
|
463
656
|
useIsMobile,
|
|
464
657
|
useSidebar,
|
|
465
|
-
NavRow
|
|
658
|
+
NavRow,
|
|
659
|
+
RAIL_COLUMN_BOX,
|
|
660
|
+
RailToggle
|
|
466
661
|
};
|