ajo-ui-playa 0.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.
Files changed (136) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +90 -0
  3. package/dist/accordion.js +41 -0
  4. package/dist/alert-dialog.js +88 -0
  5. package/dist/alert.js +49 -0
  6. package/dist/aspect-ratio.js +21 -0
  7. package/dist/attachment.js +126 -0
  8. package/dist/avatar.js +54 -0
  9. package/dist/breadcrumb.js +73 -0
  10. package/dist/bubble.js +91 -0
  11. package/dist/button-group.js +58 -0
  12. package/dist/button.js +72 -0
  13. package/dist/calendar.js +109 -0
  14. package/dist/card.js +98 -0
  15. package/dist/carousel.js +86 -0
  16. package/dist/chart.js +125 -0
  17. package/dist/checkbox-group.js +30 -0
  18. package/dist/checkbox.js +25 -0
  19. package/dist/chip.js +61 -0
  20. package/dist/chunks/modal-rE60kyB-.js +14 -0
  21. package/dist/chunks/recipes-CXxPSYdV.js +78 -0
  22. package/dist/chunks/scroll-area-BN7ME1Uj.js +14 -0
  23. package/dist/collapsible.js +35 -0
  24. package/dist/command.js +67 -0
  25. package/dist/context-menu.js +65 -0
  26. package/dist/data-table.js +16 -0
  27. package/dist/dialog.js +55 -0
  28. package/dist/direction.js +13 -0
  29. package/dist/drawer.js +61 -0
  30. package/dist/empty.js +64 -0
  31. package/dist/field.js +123 -0
  32. package/dist/index.js +306 -0
  33. package/dist/input-date.js +176 -0
  34. package/dist/input-group.js +70 -0
  35. package/dist/input-otp.js +50 -0
  36. package/dist/input.js +18 -0
  37. package/dist/item.js +126 -0
  38. package/dist/kbd.js +22 -0
  39. package/dist/label.js +14 -0
  40. package/dist/marker.js +68 -0
  41. package/dist/menu.js +66 -0
  42. package/dist/menubar.js +77 -0
  43. package/dist/message-scroller.js +76 -0
  44. package/dist/message.js +59 -0
  45. package/dist/navigation-menu.js +49 -0
  46. package/dist/pagination.js +93 -0
  47. package/dist/popover.js +20 -0
  48. package/dist/progress.js +16 -0
  49. package/dist/radio-group.js +26 -0
  50. package/dist/resizable.js +41 -0
  51. package/dist/scroll-area.js +19 -0
  52. package/dist/select.js +133 -0
  53. package/dist/separator.js +17 -0
  54. package/dist/sidebar.js +161 -0
  55. package/dist/skeleton.js +15 -0
  56. package/dist/slider.js +39 -0
  57. package/dist/spinner.js +28 -0
  58. package/dist/switch.js +26 -0
  59. package/dist/table.js +70 -0
  60. package/dist/tabs.js +61 -0
  61. package/dist/textarea.js +18 -0
  62. package/dist/toast.js +219 -0
  63. package/dist/toggle-group.js +62 -0
  64. package/dist/toggle.js +30 -0
  65. package/dist/toolbar.js +19 -0
  66. package/dist/tooltip.js +27 -0
  67. package/dist/typography.js +168 -0
  68. package/dist/virtual-list.js +17 -0
  69. package/package.json +379 -0
  70. package/src/accordion.tsx +87 -0
  71. package/src/alert-dialog.tsx +174 -0
  72. package/src/alert.tsx +121 -0
  73. package/src/aspect-ratio.tsx +39 -0
  74. package/src/attachment.tsx +255 -0
  75. package/src/avatar.tsx +131 -0
  76. package/src/breadcrumb.tsx +162 -0
  77. package/src/bubble.tsx +204 -0
  78. package/src/button-group.tsx +140 -0
  79. package/src/button.tsx +164 -0
  80. package/src/calendar.tsx +193 -0
  81. package/src/card.tsx +198 -0
  82. package/src/carousel.tsx +193 -0
  83. package/src/chart.tsx +269 -0
  84. package/src/checkbox-group.tsx +72 -0
  85. package/src/checkbox.tsx +42 -0
  86. package/src/chip.tsx +138 -0
  87. package/src/collapsible.tsx +76 -0
  88. package/src/command.tsx +137 -0
  89. package/src/context-menu.tsx +124 -0
  90. package/src/data-table.tsx +35 -0
  91. package/src/dialog.tsx +115 -0
  92. package/src/direction.tsx +29 -0
  93. package/src/drawer.tsx +104 -0
  94. package/src/empty.tsx +111 -0
  95. package/src/field.tsx +304 -0
  96. package/src/index.ts +1 -0
  97. package/src/input-date.tsx +294 -0
  98. package/src/input-group.tsx +189 -0
  99. package/src/input-otp.tsx +106 -0
  100. package/src/input.tsx +29 -0
  101. package/src/internal/recipes.tsx +131 -0
  102. package/src/internal/scroll-area.tsx +19 -0
  103. package/src/item.tsx +218 -0
  104. package/src/kbd.tsx +24 -0
  105. package/src/label.tsx +28 -0
  106. package/src/marker.tsx +120 -0
  107. package/src/menu.tsx +136 -0
  108. package/src/menubar.tsx +138 -0
  109. package/src/message-scroller.tsx +168 -0
  110. package/src/message.tsx +111 -0
  111. package/src/modal.tsx +17 -0
  112. package/src/navigation-menu.tsx +93 -0
  113. package/src/pagination.tsx +196 -0
  114. package/src/popover.tsx +30 -0
  115. package/src/progress.tsx +39 -0
  116. package/src/radio-group.tsx +62 -0
  117. package/src/resizable.tsx +111 -0
  118. package/src/scroll-area.tsx +32 -0
  119. package/src/select.tsx +225 -0
  120. package/src/separator.tsx +34 -0
  121. package/src/sidebar.tsx +405 -0
  122. package/src/skeleton.tsx +28 -0
  123. package/src/slider.tsx +59 -0
  124. package/src/spinner.tsx +41 -0
  125. package/src/styles.ts +434 -0
  126. package/src/switch.tsx +45 -0
  127. package/src/table.tsx +149 -0
  128. package/src/tabs.tsx +176 -0
  129. package/src/textarea.tsx +30 -0
  130. package/src/toast.tsx +416 -0
  131. package/src/toggle-group.tsx +128 -0
  132. package/src/toggle.tsx +62 -0
  133. package/src/toolbar.tsx +26 -0
  134. package/src/tooltip.tsx +51 -0
  135. package/src/typography.tsx +231 -0
  136. package/src/virtual-list.tsx +40 -0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2022-2026, Cristian Falcone
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # ajo-ui-playa
2
+
3
+ Themed Ajo component library and UnoCSS preset.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add ajo ajo-ui-playa
9
+ pnpm add -D unocss@66.7.2
10
+ ```
11
+
12
+ `ajo-ui-playa` requires `ajo ^0.1.35` and `unocss 66.7.2`.
13
+
14
+ ## UnoCSS Setup
15
+
16
+ Add `playa()` to the application's UnoCSS config:
17
+
18
+ ```ts
19
+ // uno.config.ts
20
+ import { playa } from 'ajo-ui-playa'
21
+ import { defineConfig } from 'unocss'
22
+
23
+ export default defineConfig({
24
+ presets: [playa()],
25
+ })
26
+ ```
27
+
28
+ Add the UnoCSS plugin to the application's Vite config:
29
+
30
+ ```ts
31
+ // vite.config.ts
32
+ import { defineConfig } from 'vite'
33
+ import unocss from 'unocss/vite'
34
+
35
+ export default defineConfig({
36
+ plugins: [unocss()],
37
+ })
38
+ ```
39
+
40
+ Load the generated stylesheet from the application entry:
41
+
42
+ ```ts
43
+ import 'virtual:uno.css'
44
+ ```
45
+
46
+ ### With ajo-kit
47
+
48
+ `ajo-kit` can load the stylesheet before hydration:
49
+
50
+ ```ts
51
+ import { kit, jsx } from 'ajo-kit/vite'
52
+ import { defineConfig } from 'vite'
53
+ import unocss from 'unocss/vite'
54
+
55
+ export default defineConfig({
56
+ plugins: [...kit({ css: ['virtual:uno.css'] }), unocss()],
57
+ esbuild: jsx,
58
+ })
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ Import `playa()` from the package root. Import components from family subpaths:
64
+
65
+ ```tsx
66
+ import Button, { buttonVariants } from 'ajo-ui-playa/button'
67
+ import { Card, CardContent } from 'ajo-ui-playa/card'
68
+ import { DataTable, type DataTableColumn } from 'ajo-ui-playa/data-table'
69
+ ```
70
+
71
+ Family subpath imports are side-effect-free and tree-shakeable.
72
+
73
+ ## Components
74
+
75
+ | Group | Family subpaths |
76
+ |---|---|
77
+ | Actions and status | `alert`, `alert-dialog`, `button`, `button-group`, `chip`, `marker`, `spinner` |
78
+ | Content and layout | `aspect-ratio`, `attachment`, `breadcrumb`, `bubble`, `card`, `empty`, `item`, `kbd`, `label`, `pagination`, `scroll-area`, `separator`, `skeleton`, `table`, `typography` |
79
+ | Inputs and selection | `checkbox`, `checkbox-group`, `field`, `input`, `input-date`, `input-group`, `input-otp`, `radio-group`, `select`, `slider`, `switch`, `textarea`, `toggle`, `toggle-group` |
80
+ | Navigation and overlays | `accordion`, `collapsible`, `command`, `context-menu`, `dialog`, `direction`, `drawer`, `menu`, `menubar`, `navigation-menu`, `popover`, `sidebar`, `tabs`, `toast`, `toolbar`, `tooltip` |
81
+ | Data and media | `avatar`, `calendar`, `carousel`, `chart`, `data-table`, `message`, `message-scroller`, `progress`, `resizable`, `virtual-list` |
82
+
83
+ ## UnoCSS Preset
84
+
85
+ `playa()` configures Wind4, Lucide icons, Playa design tokens, preflights,
86
+ variants, rules, and component shortcuts.
87
+
88
+ UnoCSS discovers classes from imported component families. Define application
89
+ shortcuts in `uno.config.ts` and add dynamically generated icon names to the
90
+ application safelist.
@@ -0,0 +1,41 @@
1
+ // @unocss-include
2
+ import { c as disclosureContent } from "./chunks/recipes-CXxPSYdV.js";
3
+ import clsx from "clsx";
4
+ import { Accordion as Accordion$1, AccordionContent as AccordionContent$1, AccordionItem as AccordionItem$1, AccordionTrigger as AccordionTrigger$1 } from "ajo-ui/accordion";
5
+ import { jsx, jsxs } from "ajo/jsx-runtime";
6
+ //#region packages/ajo-ui-playa/src/accordion.tsx
7
+ var itemBase = clsx("border-b last:border-b-0", disclosureContent);
8
+ var triggerBase = "flex flex-1 cursor-pointer list-none items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none focus-visible:ring-3 focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&::-webkit-details-marker]:hidden [&[data-state=open]>[data-accordion-chevron]]:rotate-180";
9
+ /** Root provider for accordion state. */
10
+ var Accordion = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx(Accordion$1, {
11
+ ...attrs,
12
+ class: classes
13
+ });
14
+ /** Accordion section rendered as a native details element. */
15
+ var AccordionItem = ({ children, class: classes, disabled, open: _open, value, ...attrs }) => /* @__PURE__ */ jsx(AccordionItem$1, {
16
+ ...attrs,
17
+ class: clsx(itemBase, classes),
18
+ disabled,
19
+ value,
20
+ children
21
+ });
22
+ /** Accordion heading trigger rendered as a native summary element. */
23
+ var AccordionTrigger = ({ children, class: classes, "set:onclick": onClick, ...attrs }) => /* @__PURE__ */ jsxs(AccordionTrigger$1, {
24
+ ...attrs,
25
+ class: clsx(triggerBase, classes),
26
+ "set:onclick": onClick,
27
+ children: [children, /* @__PURE__ */ jsx("span", {
28
+ "aria-hidden": "true",
29
+ "data-accordion-chevron": true,
30
+ class: "i-lucide-chevron-down pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200"
31
+ })]
32
+ });
33
+ /** Accordion panel content. */
34
+ var AccordionContent = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx(AccordionContent$1, {
35
+ ...attrs,
36
+ class: "overflow-hidden text-sm",
37
+ innerClass: clsx("pb-4 pt-0", classes),
38
+ children
39
+ });
40
+ //#endregion
41
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,88 @@
1
+ // @unocss-include
2
+ import { buttonVariants } from "./button.js";
3
+ import { DialogContent as DialogContent$1, DialogDescription as DialogDescription$1 } from "./dialog.js";
4
+ import clsx from "clsx";
5
+ import { jsx } from "ajo/jsx-runtime";
6
+ import { Dialog, DialogClose, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "ajo-ui/dialog";
7
+ //#region packages/ajo-ui-playa/src/alert-dialog.tsx
8
+ var contentBase = "data-[size=sm]:max-w-xs sm:data-[size=default]:max-w-lg";
9
+ var headerBase = "grid grid-rows-[auto_1fr] place-items-center text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:[[data-slot=alert-dialog-content][data-size=default]_&]:place-items-start sm:[[data-slot=alert-dialog-content][data-size=default]_&]:text-left sm:[[data-slot=alert-dialog-content][data-size=default]_&:has([data-slot=alert-dialog-media])]:grid-rows-[auto_1fr]";
10
+ var footerBase = "flex flex-col-reverse gap-2 [[data-slot=alert-dialog-content][data-size=sm]_&]:grid [[data-slot=alert-dialog-content][data-size=sm]_&]:grid-cols-2 sm:flex-row sm:justify-end";
11
+ var titleBase = "text-lg font-semibold sm:[[data-slot=alert-dialog-content][data-size=default]:has([data-slot=alert-dialog-media])_&]:col-start-2";
12
+ var mediaBase = "mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted sm:[[data-slot=alert-dialog-content][data-size=default]_&]:row-span-2 [&>svg:not([class*=size-])]:size-8";
13
+ /** Root provider for a modal alert dialog that requires a user response. */
14
+ var AlertDialog = (attrs) => /* @__PURE__ */ jsx(Dialog, {
15
+ ...attrs,
16
+ modal: true,
17
+ "data-slot": "alert-dialog"
18
+ });
19
+ /** Button that opens the nearest AlertDialog. */
20
+ var AlertDialogTrigger = (attrs) => /* @__PURE__ */ jsx(DialogTrigger, {
21
+ ...attrs,
22
+ "data-slot": "alert-dialog-trigger"
23
+ });
24
+ /** Native modal alert dialog panel. */
25
+ var AlertDialogContent = ({ class: classes, size = "default", ...attrs }) => /* @__PURE__ */ jsx(DialogContent$1, {
26
+ ...attrs,
27
+ class: clsx(contentBase, classes),
28
+ "data-size": size,
29
+ "data-slot": "alert-dialog-content",
30
+ onPointerDownOutside: (event) => event.preventDefault(),
31
+ role: "alertdialog",
32
+ showCloseButton: false,
33
+ unstyled: false
34
+ });
35
+ /** Header area for alert dialog title, description, and optional media. */
36
+ var AlertDialogHeader = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx(DialogHeader, {
37
+ ...attrs,
38
+ class: clsx(headerBase, classes),
39
+ "data-slot": "alert-dialog-header"
40
+ });
41
+ /** Footer area for alert dialog cancel and action buttons. */
42
+ var AlertDialogFooter = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx(DialogFooter, {
43
+ ...attrs,
44
+ class: clsx(footerBase, classes),
45
+ "data-slot": "alert-dialog-footer"
46
+ });
47
+ /** Accessible title for AlertDialogContent. */
48
+ var AlertDialogTitle = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx(DialogTitle, {
49
+ ...attrs,
50
+ class: clsx(titleBase, classes),
51
+ "data-slot": "alert-dialog-title"
52
+ });
53
+ /** Accessible description for AlertDialogContent. */
54
+ var AlertDialogDescription = (attrs) => /* @__PURE__ */ jsx(DialogDescription$1, {
55
+ ...attrs,
56
+ "data-slot": "alert-dialog-description"
57
+ });
58
+ /** Optional media block for an icon or image in the alert dialog header. */
59
+ var AlertDialogMedia = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
60
+ ...attrs,
61
+ class: clsx(mediaBase, classes),
62
+ "data-slot": "alert-dialog-media",
63
+ children
64
+ });
65
+ /** Primary alert dialog action button. Closes unless its click handler prevents default. */
66
+ var AlertDialogAction = ({ children, class: classes, size = "default", variant = "default", ...attrs }) => /* @__PURE__ */ jsx(DialogClose, {
67
+ ...attrs,
68
+ class: buttonVariants({
69
+ class: classes,
70
+ size,
71
+ variant
72
+ }),
73
+ "data-slot": "alert-dialog-action",
74
+ children
75
+ });
76
+ /** Cancel button. Closes unless its click handler prevents default. */
77
+ var AlertDialogCancel = ({ children, class: classes, size = "default", variant = "outline", ...attrs }) => /* @__PURE__ */ jsx(DialogClose, {
78
+ ...attrs,
79
+ class: buttonVariants({
80
+ class: classes,
81
+ size,
82
+ variant
83
+ }),
84
+ "data-slot": "alert-dialog-cancel",
85
+ children
86
+ });
87
+ //#endregion
88
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogTrigger };
package/dist/alert.js ADDED
@@ -0,0 +1,49 @@
1
+ // @unocss-include
2
+ import clsx from "clsx";
3
+ import { jsx } from "ajo/jsx-runtime";
4
+ //#region packages/ajo-ui-playa/src/alert.tsx
5
+ var base = "relative grid w-full grid-cols-[0_1fr_auto] items-start gap-y-0.5 rounded-lg edge px-4 py-3 text-sm has-[>svg]:grid-cols-[1rem_1fr_auto] has-[>svg]:gap-x-3 has-[>[data-slot=alert-icon]]:grid-cols-[1rem_1fr_auto] has-[>[data-slot=alert-icon]]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current [&>[data-slot=alert-icon]]:size-4 [&>[data-slot=alert-icon]]:translate-y-0.5 [&>[data-slot=alert-icon]]:text-current";
6
+ var variants = {
7
+ default: "bg-card/80 text-card-foreground shadow-xs",
8
+ danger: "bg-danger/10 text-danger inset-ring-danger/25 [&_[data-slot=alert-description]]:text-danger/85",
9
+ success: "bg-success/10 text-success inset-ring-success/25 [&_[data-slot=alert-description]]:text-success/85",
10
+ warning: "bg-warning/10 text-warning inset-ring-warning/25 [&_[data-slot=alert-description]]:text-warning/85",
11
+ info: "bg-info/10 text-info inset-ring-info/25 [&_[data-slot=alert-description]]:text-info/85"
12
+ };
13
+ /** Returns the UnoCSS class list for an alert variant. */
14
+ var alertVariants = ({ class: classes, variant = "default" } = {}) => clsx(base, variants[variant], classes);
15
+ /** Callout for important user attention. */
16
+ var Alert = ({ class: classes, children, role = "alert", variant = "default", ...attrs }) => /* @__PURE__ */ jsx("div", {
17
+ ...attrs,
18
+ class: alertVariants({
19
+ class: classes,
20
+ variant
21
+ }),
22
+ "data-slot": "alert",
23
+ "data-variant": variant,
24
+ role,
25
+ children
26
+ });
27
+ /** Title slot for `Alert`. */
28
+ var AlertTitle = ({ class: classes, children, ...attrs }) => /* @__PURE__ */ jsx("div", {
29
+ ...attrs,
30
+ class: clsx("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", classes),
31
+ "data-slot": "alert-title",
32
+ children
33
+ });
34
+ /** Description/content slot for `Alert`. */
35
+ var AlertDescription = ({ class: classes, children, ...attrs }) => /* @__PURE__ */ jsx("div", {
36
+ ...attrs,
37
+ class: clsx("col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed", classes),
38
+ "data-slot": "alert-description",
39
+ children
40
+ });
41
+ /** Action slot for `Alert`, aligned to the end on wider screens. */
42
+ var AlertAction = ({ class: classes, children, ...attrs }) => /* @__PURE__ */ jsx("div", {
43
+ ...attrs,
44
+ class: clsx("col-start-2 mt-3 flex flex-wrap gap-2 sm:col-start-3 sm:row-span-2 sm:row-start-1 sm:mt-0 sm:justify-self-end sm:ps-3", classes),
45
+ "data-slot": "alert-action",
46
+ children
47
+ });
48
+ //#endregion
49
+ export { Alert, Alert as default, AlertAction, AlertDescription, AlertTitle, alertVariants };
@@ -0,0 +1,21 @@
1
+ // @unocss-include
2
+ import clsx from "clsx";
3
+ import { jsx } from "ajo/jsx-runtime";
4
+ import { stlx } from "ajo-ui/utils";
5
+ //#region packages/ajo-ui-playa/src/aspect-ratio.tsx
6
+ var base = "relative w-full";
7
+ var ratioValue = (ratio) => Number.isFinite(ratio) && ratio > 0 ? String(ratio) : "1";
8
+ /** Responsive box that preserves a width-to-height ratio. */
9
+ var AspectRatio = ({ class: classes, children, ratio, style, ...attrs }) => {
10
+ const value = ratioValue(ratio);
11
+ return /* @__PURE__ */ jsx("div", {
12
+ ...attrs,
13
+ class: clsx(base, classes),
14
+ "data-ratio": value,
15
+ "data-slot": "aspect-ratio",
16
+ style: stlx(style, { aspectRatio: value }),
17
+ children
18
+ });
19
+ };
20
+ //#endregion
21
+ export { AspectRatio, AspectRatio as default };
@@ -0,0 +1,126 @@
1
+ // @unocss-include
2
+ import { buttonVariants } from "./button.js";
3
+ import clsx from "clsx";
4
+ import { jsx } from "ajo/jsx-runtime";
5
+ //#region packages/ajo-ui-playa/src/attachment.tsx
6
+ var rootBase = [
7
+ "group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-xl glass edge shadow-xs transition-colors",
8
+ "focus-within:ring-1 focus-within:ring-ring/50 has-[>a,>button]:hover:bg-muted/50",
9
+ "data-[state=error]:inset-ring-danger/25 data-[state=idle]:inset-ring-transparent data-[state=idle]:border data-[state=idle]:border-dashed"
10
+ ].join(" ");
11
+ var rootSizes = {
12
+ default: "gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2",
13
+ sm: "gap-2 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5",
14
+ xs: "gap-1.5 rounded-lg text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1"
15
+ };
16
+ var rootOrientations = {
17
+ horizontal: "min-w-40 items-center",
18
+ vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30"
19
+ };
20
+ var mediaBase = [
21
+ "relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted text-foreground",
22
+ "group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=sm]/attachment:rounded-sm group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-sm",
23
+ "group-data-[state=error]/attachment:bg-danger/10 group-data-[state=error]/attachment:text-danger",
24
+ "[&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4",
25
+ "group-data-[orientation=vertical]/attachment:[&_svg:not([class*=size-])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*=size-])]:size-3.5"
26
+ ].join(" ");
27
+ var mediaVariants = {
28
+ icon: "",
29
+ image: "opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 [&>img]:aspect-square [&>img]:size-full [&>img]:object-cover"
30
+ };
31
+ var contentBase = "max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-1";
32
+ var titleBase = "block max-w-full min-w-0 truncate font-medium";
33
+ var descriptionBase = "mt-0.5 block max-w-full min-w-0 truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-danger/80";
34
+ var actionsBase = "relative z-20 flex shrink-0 items-center group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:right-3 group-data-[orientation=vertical]/attachment:top-3 group-data-[orientation=vertical]/attachment:gap-1";
35
+ var triggerBase = "absolute inset-0 z-10 outline-none focus-visible:ring-3 focus-visible:ring-ring/50";
36
+ var groupBase = "flex min-w-0 items-start scroll-fade-x snap-x snap-mandatory scroll-px-4 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain px-4 py-1 [&>[data-slot=attachment]]:flex-none [&>[data-slot=attachment]]:snap-start";
37
+ /** Root attachment container for files, images, upload rows, and chat attachments. */
38
+ var Attachment = ({ children, class: classes, orientation = "horizontal", size = "default", state = "done", ...attrs }) => /* @__PURE__ */ jsx("div", {
39
+ ...attrs,
40
+ class: clsx(rootBase, rootSizes[size], rootOrientations[orientation], classes),
41
+ "data-orientation": orientation,
42
+ "data-size": size,
43
+ "data-slot": "attachment",
44
+ "data-state": state,
45
+ children
46
+ });
47
+ /** Media slot for icons, thumbnails, or image previews. */
48
+ var AttachmentMedia = ({ children, class: classes, variant = "icon", ...attrs }) => /* @__PURE__ */ jsx("div", {
49
+ ...attrs,
50
+ class: clsx(mediaBase, mediaVariants[variant], classes),
51
+ "data-slot": "attachment-media",
52
+ "data-variant": variant,
53
+ children
54
+ });
55
+ /** Content slot wrapping title and description. */
56
+ var AttachmentContent = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
57
+ ...attrs,
58
+ class: clsx(contentBase, classes),
59
+ "data-slot": "attachment-content",
60
+ children
61
+ });
62
+ /** Attachment display name. */
63
+ var AttachmentTitle = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("span", {
64
+ ...attrs,
65
+ class: clsx(titleBase, classes),
66
+ "data-slot": "attachment-title",
67
+ children
68
+ });
69
+ /** Secondary metadata such as type, size, upload status, or error reason. */
70
+ var AttachmentDescription = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("span", {
71
+ ...attrs,
72
+ class: clsx(descriptionBase, classes),
73
+ "data-slot": "attachment-description",
74
+ children
75
+ });
76
+ /** Action container aligned to the edge of the attachment. */
77
+ var AttachmentActions = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
78
+ ...attrs,
79
+ class: clsx(actionsBase, classes),
80
+ "data-slot": "attachment-actions",
81
+ children
82
+ });
83
+ /** Icon-sized action button for attachment operations. */
84
+ var AttachmentAction = ({ children, class: classes, size = "icon-xs", type = "button", variant = "ghost", ...attrs }) => /* @__PURE__ */ jsx("button", {
85
+ ...attrs,
86
+ class: buttonVariants({
87
+ class: classes,
88
+ size,
89
+ variant
90
+ }),
91
+ "data-size": size,
92
+ "data-slot": "attachment-action",
93
+ "data-variant": variant,
94
+ type,
95
+ children
96
+ });
97
+ /** Full-card trigger layered behind actions. */
98
+ var AttachmentTrigger = ({ as = "button", children, class: classes, type = "button", ...attrs }) => {
99
+ const styles = clsx(triggerBase, classes);
100
+ if (as === "a") {
101
+ const anchor = attrs;
102
+ return /* @__PURE__ */ jsx("a", {
103
+ ...anchor,
104
+ class: styles,
105
+ "data-slot": "attachment-trigger",
106
+ href: String(anchor.href),
107
+ children
108
+ });
109
+ }
110
+ return /* @__PURE__ */ jsx("button", {
111
+ ...attrs,
112
+ class: styles,
113
+ "data-slot": "attachment-trigger",
114
+ type,
115
+ children
116
+ });
117
+ };
118
+ /** Horizontally scrollable attachment row with snap points and edge fade. */
119
+ var AttachmentGroup = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
120
+ ...attrs,
121
+ class: clsx(groupBase, classes),
122
+ "data-slot": "attachment-group",
123
+ children
124
+ });
125
+ //#endregion
126
+ export { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger };
package/dist/avatar.js ADDED
@@ -0,0 +1,54 @@
1
+ // @unocss-include
2
+ import clsx from "clsx";
3
+ import { jsx } from "ajo/jsx-runtime";
4
+ import { Avatar as Avatar$1, AvatarBadge as AvatarBadge$1, AvatarFallback as AvatarFallback$1, AvatarGroup as AvatarGroup$1, AvatarGroupCount as AvatarGroupCount$1, AvatarImage as AvatarImage$1 } from "ajo-ui/avatar";
5
+ //#region packages/ajo-ui-playa/src/avatar.tsx
6
+ var avatarBase = "group/avatar relative flex size-8 shrink-0 rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6";
7
+ var imageBase = "absolute inset-0 aspect-square size-full rounded-full object-cover";
8
+ var fallbackBase = "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs group-data-[size=lg]/avatar:text-base";
9
+ var badgeBase = [
10
+ "absolute bottom-0 right-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground ring-2 ring-background select-none",
11
+ "[&:not(:has(>*))]:size-2.5 group-data-[size=sm]/avatar:[&:not(:has(>*))]:size-2 group-data-[size=lg]/avatar:[&:not(:has(>*))]:size-3",
12
+ "[&:has(>*)]:size-4 group-data-[size=lg]/avatar:[&:has(>*)]:size-5 group-data-[size=sm]/avatar:[&:has(>*)]:hidden",
13
+ "[&>*]:size-2.5 group-data-[size=lg]/avatar:[&>*]:size-3"
14
+ ].join(" ");
15
+ var groupBase = "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background";
16
+ var groupCountBase = "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background [[data-slot=avatar-group]:has([data-size=lg])_&]:size-10 [[data-slot=avatar-group]:has([data-size=sm])_&]:size-6 [&>svg]:size-4 [[data-slot=avatar-group]:has([data-size=lg])_&>svg]:size-5 [[data-slot=avatar-group]:has([data-size=sm])_&>svg]:size-3";
17
+ /** Avatar root that wraps image, fallback, and optional badge. */
18
+ var Avatar = ({ children, class: classes, size = "default", ...attrs }) => /* @__PURE__ */ jsx(Avatar$1, {
19
+ ...attrs,
20
+ class: clsx(avatarBase, classes),
21
+ size,
22
+ children
23
+ });
24
+ /** Avatar image. It hides itself after load errors so the fallback remains visible. */
25
+ var AvatarImage = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx(AvatarImage$1, {
26
+ ...attrs,
27
+ class: clsx(imageBase, classes)
28
+ });
29
+ /** Avatar fallback content shown behind the image and after image errors. */
30
+ var AvatarFallback = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx(AvatarFallback$1, {
31
+ ...attrs,
32
+ class: clsx(fallbackBase, classes),
33
+ children
34
+ });
35
+ /** Small status badge positioned at the bottom-right of an avatar. */
36
+ var AvatarBadge = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx(AvatarBadge$1, {
37
+ ...attrs,
38
+ class: clsx(badgeBase, classes),
39
+ children
40
+ });
41
+ /** Overlapping avatar group. */
42
+ var AvatarGroup = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx(AvatarGroup$1, {
43
+ ...attrs,
44
+ class: clsx(groupBase, classes),
45
+ children
46
+ });
47
+ /** Count avatar for extra members in an avatar group. */
48
+ var AvatarGroupCount = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx(AvatarGroupCount$1, {
49
+ ...attrs,
50
+ class: clsx(groupCountBase, classes),
51
+ children
52
+ });
53
+ //#endregion
54
+ export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage };
@@ -0,0 +1,73 @@
1
+ // @unocss-include
2
+ import clsx from "clsx";
3
+ import { jsx, jsxs } from "ajo/jsx-runtime";
4
+ import { emptyChildren } from "ajo-ui/utils";
5
+ //#region packages/ajo-ui-playa/src/breadcrumb.tsx
6
+ /** Landmark wrapper for breadcrumb navigation. */
7
+ var Breadcrumb = ({ children, "aria-label": ariaLabel = "breadcrumb", class: classes, ...attrs }) => /* @__PURE__ */ jsx("nav", {
8
+ ...attrs,
9
+ "aria-label": ariaLabel,
10
+ class: classes,
11
+ "data-slot": "breadcrumb",
12
+ children
13
+ });
14
+ /** Ordered breadcrumb item list. */
15
+ var BreadcrumbList = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("ol", {
16
+ ...attrs,
17
+ class: clsx("flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", classes),
18
+ "data-slot": "breadcrumb-list",
19
+ children
20
+ });
21
+ /** Single breadcrumb list item. */
22
+ var BreadcrumbItem = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("li", {
23
+ ...attrs,
24
+ class: clsx("inline-flex items-center gap-1.5", classes),
25
+ "data-slot": "breadcrumb-item",
26
+ children
27
+ });
28
+ /** Clickable breadcrumb link. */
29
+ var BreadcrumbLink = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("a", {
30
+ ...attrs,
31
+ class: clsx("transition-colors hover:text-foreground", classes),
32
+ "data-slot": "breadcrumb-link",
33
+ children
34
+ });
35
+ /** Current page marker inside a breadcrumb. */
36
+ var BreadcrumbPage = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("span", {
37
+ ...attrs,
38
+ "aria-current": "page",
39
+ "aria-disabled": "true",
40
+ class: clsx("font-normal text-foreground", classes),
41
+ "data-slot": "breadcrumb-page",
42
+ role: "link",
43
+ children
44
+ });
45
+ /** Decorative separator between breadcrumb items. */
46
+ var BreadcrumbSeparator = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("li", {
47
+ ...attrs,
48
+ "aria-hidden": "true",
49
+ class: clsx("inline-flex items-center justify-center [&>svg]:size-3.5", classes),
50
+ "data-slot": "breadcrumb-separator",
51
+ role: "presentation",
52
+ children: emptyChildren(children) ? /* @__PURE__ */ jsx("span", {
53
+ "aria-hidden": "true",
54
+ class: "i-lucide-chevron-right block size-3.5 shrink-0 text-muted-foreground"
55
+ }) : children
56
+ });
57
+ /** Collapsed breadcrumb range indicator. */
58
+ var BreadcrumbEllipsis = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsxs("span", {
59
+ ...attrs,
60
+ "aria-hidden": "true",
61
+ class: clsx("flex size-9 items-center justify-center", classes),
62
+ "data-slot": "breadcrumb-ellipsis",
63
+ role: "presentation",
64
+ children: [/* @__PURE__ */ jsx("span", {
65
+ "aria-hidden": "true",
66
+ class: "i-lucide-ellipsis size-4"
67
+ }), /* @__PURE__ */ jsx("span", {
68
+ class: "sr-only",
69
+ children: "More"
70
+ })]
71
+ });
72
+ //#endregion
73
+ export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator };
package/dist/bubble.js ADDED
@@ -0,0 +1,91 @@
1
+ // @unocss-include
2
+ import clsx from "clsx";
3
+ import { jsx } from "ajo/jsx-runtime";
4
+ //#region packages/ajo-ui-playa/src/bubble.tsx
5
+ var groupBase = [
6
+ "flex min-w-0 flex-col gap-1",
7
+ "[&>[data-slot=bubble]:not(:last-child)>[data-slot=bubble-content]]:rounded-bl-2xl",
8
+ "[&>[data-slot=bubble]:not(:last-child)>[data-slot=bubble-content]]:rounded-br-2xl"
9
+ ].join(" ");
10
+ var rootBase = ["group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1", "group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full"].join(" ");
11
+ var rootVariants = {
12
+ default: "*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/90",
13
+ secondary: "*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-secondary/80",
14
+ muted: "*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/80",
15
+ tinted: "*:data-[slot=bubble-content]:bg-primary/10 *:data-[slot=bubble-content]:text-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/15",
16
+ outline: "*:data-[slot=bubble-content]:bg-card/80 *:data-[slot=bubble-content]:text-card-foreground *:data-[slot=bubble-content]:inset-ring *:data-[slot=bubble-content]:inset-ring-border [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground",
17
+ ghost: "*:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-accent [&>[data-slot=bubble-content]:is(button,a):hover]:text-accent-foreground",
18
+ danger: "*:data-[slot=bubble-content]:bg-danger/10 *:data-[slot=bubble-content]:text-danger *:data-[slot=bubble-content]:inset-ring *:data-[slot=bubble-content]:inset-ring-danger/25 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-danger/20"
19
+ };
20
+ var contentBase = [
21
+ "w-fit max-w-full min-w-0 overflow-hidden px-3.5 py-2 text-sm leading-relaxed [overflow-wrap:anywhere]",
22
+ "rounded-2xl rounded-bl-md",
23
+ "[[data-slot=bubble][data-align=end]_&]:rounded-bl-2xl [[data-slot=bubble][data-align=end]_&]:rounded-br-md",
24
+ "[[data-slot=message][data-align=end]_&]:rounded-bl-2xl [[data-slot=message][data-align=end]_&]:rounded-br-md",
25
+ "[[data-slot=bubble][data-variant=ghost]_&]:rounded-none",
26
+ "group-data-[align=end]/bubble:self-end",
27
+ "[&:is(button)]:text-left [&:is(button,a)]:transition-colors [&:is(button,a)]:outline-none [&:is(button,a)]:focus-visible:ring-3 [&:is(button,a)]:focus-visible:ring-ring/50"
28
+ ].join(" ");
29
+ var reactionsBase = "absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-popover text-popover-foreground edge px-1.5 py-0.5 text-sm has-[button]:p-0";
30
+ var reactionSides = {
31
+ bottom: "bottom-0 translate-y-3/4",
32
+ top: "top-0 -translate-y-3/4"
33
+ };
34
+ var reactionAligns = {
35
+ end: "right-3",
36
+ start: "left-3"
37
+ };
38
+ /** Groups consecutive bubbles from one speaker. */
39
+ var BubbleGroup = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
40
+ ...attrs,
41
+ class: clsx(groupBase, classes),
42
+ "data-slot": "bubble-group",
43
+ children
44
+ });
45
+ /** Presentational chat bubble wrapper. Keep message-level semantics outside this component. */
46
+ var Bubble = ({ align = "start", children, class: classes, variant = "default", ...attrs }) => /* @__PURE__ */ jsx("div", {
47
+ ...attrs,
48
+ class: clsx(rootBase, rootVariants[variant], classes),
49
+ "data-align": align,
50
+ "data-slot": "bubble",
51
+ "data-variant": variant,
52
+ children
53
+ });
54
+ /** Bubble content surface. Use `as="button"` or `as="a"` for interactive bubbles. */
55
+ var BubbleContent = ({ as = "div", children, class: classes, type = "button", ...attrs }) => {
56
+ const styles = clsx(contentBase, classes);
57
+ if (as === "a") {
58
+ const anchor = attrs;
59
+ return /* @__PURE__ */ jsx("a", {
60
+ ...anchor,
61
+ class: styles,
62
+ "data-slot": "bubble-content",
63
+ href: String(anchor.href),
64
+ children
65
+ });
66
+ }
67
+ if (as === "button") return /* @__PURE__ */ jsx("button", {
68
+ ...attrs,
69
+ class: styles,
70
+ "data-slot": "bubble-content",
71
+ type,
72
+ children
73
+ });
74
+ return /* @__PURE__ */ jsx("div", {
75
+ ...attrs,
76
+ class: styles,
77
+ "data-slot": "bubble-content",
78
+ children
79
+ });
80
+ };
81
+ /** Reaction row anchored to a bubble edge. */
82
+ var BubbleReactions = ({ align = "end", children, class: classes, side = "bottom", ...attrs }) => /* @__PURE__ */ jsx("div", {
83
+ ...attrs,
84
+ class: clsx(reactionsBase, reactionSides[side], reactionAligns[align], classes),
85
+ "data-align": align,
86
+ "data-side": side,
87
+ "data-slot": "bubble-reactions",
88
+ children
89
+ });
90
+ //#endregion
91
+ export { Bubble, BubbleContent, BubbleGroup, BubbleReactions };