opacacms 0.1.1 → 0.1.2

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 (212) hide show
  1. package/package.json +36 -1
  2. package/bun.lock +0 -34
  3. package/global.d.ts +0 -11
  4. package/src/admin/api-client.ts +0 -63
  5. package/src/admin/auth-client.ts +0 -40
  6. package/src/admin/custom-field.ts +0 -179
  7. package/src/admin/index.ts +0 -15
  8. package/src/admin/react.tsx +0 -72
  9. package/src/admin/router.ts +0 -9
  10. package/src/admin/stores/admin-queries.ts +0 -121
  11. package/src/admin/stores/auth.ts +0 -61
  12. package/src/admin/stores/column-visibility.ts +0 -67
  13. package/src/admin/stores/config.ts +0 -15
  14. package/src/admin/stores/media.ts +0 -95
  15. package/src/admin/stores/query.ts +0 -13
  16. package/src/admin/stores/ui.ts +0 -29
  17. package/src/admin/ui/admin-client.tsx +0 -283
  18. package/src/admin/ui/admin-layout.tsx +0 -276
  19. package/src/admin/ui/components/ColumnVisibilityToggle.tsx +0 -141
  20. package/src/admin/ui/components/DataDetailSheet.tsx +0 -141
  21. package/src/admin/ui/components/DataDetailView.tsx +0 -175
  22. package/src/admin/ui/components/Table.tsx +0 -67
  23. package/src/admin/ui/components/fields/ArrayField.tsx +0 -166
  24. package/src/admin/ui/components/fields/BlocksField.tsx +0 -202
  25. package/src/admin/ui/components/fields/BooleanField.tsx +0 -50
  26. package/src/admin/ui/components/fields/CollapsibleField.tsx +0 -75
  27. package/src/admin/ui/components/fields/DateField.tsx +0 -45
  28. package/src/admin/ui/components/fields/FileField.tsx +0 -322
  29. package/src/admin/ui/components/fields/GroupField.tsx +0 -50
  30. package/src/admin/ui/components/fields/JoinField.tsx +0 -23
  31. package/src/admin/ui/components/fields/NumberField.tsx +0 -46
  32. package/src/admin/ui/components/fields/RadioField.tsx +0 -62
  33. package/src/admin/ui/components/fields/RelationshipField.tsx +0 -278
  34. package/src/admin/ui/components/fields/RowField.tsx +0 -40
  35. package/src/admin/ui/components/fields/SelectField.tsx +0 -59
  36. package/src/admin/ui/components/fields/TabsField.tsx +0 -101
  37. package/src/admin/ui/components/fields/TextAreaField.tsx +0 -54
  38. package/src/admin/ui/components/fields/TextField.tsx +0 -49
  39. package/src/admin/ui/components/fields/VirtualField.tsx +0 -53
  40. package/src/admin/ui/components/fields/index.tsx +0 -371
  41. package/src/admin/ui/components/fields/richtext-editor/index.tsx +0 -211
  42. package/src/admin/ui/components/fields/richtext-editor/nodes/ImageComponent.tsx +0 -142
  43. package/src/admin/ui/components/fields/richtext-editor/nodes/ImageNode.tsx +0 -95
  44. package/src/admin/ui/components/fields/richtext-editor/plugins/ComponentPickerPlugin.tsx +0 -226
  45. package/src/admin/ui/components/fields/richtext-editor/plugins/EditableSyncPlugin.tsx +0 -16
  46. package/src/admin/ui/components/fields/richtext-editor/plugins/NotionToolbarPlugin.tsx +0 -184
  47. package/src/admin/ui/components/fields/richtext-editor/plugins/SimpleToolbarPlugin.tsx +0 -240
  48. package/src/admin/ui/components/fields/richtext-editor/plugins/ValueSyncPlugin.tsx +0 -40
  49. package/src/admin/ui/components/fields/utils.ts +0 -1
  50. package/src/admin/ui/components/link.tsx +0 -41
  51. package/src/admin/ui/components/media/AssetManagerModal.tsx +0 -334
  52. package/src/admin/ui/components/toast.tsx +0 -72
  53. package/src/admin/ui/components/ui/accordion.tsx +0 -51
  54. package/src/admin/ui/components/ui/alert-dialog.tsx +0 -98
  55. package/src/admin/ui/components/ui/blocks.tsx +0 -32
  56. package/src/admin/ui/components/ui/breadcrumbs.tsx +0 -59
  57. package/src/admin/ui/components/ui/button.tsx +0 -26
  58. package/src/admin/ui/components/ui/collapsible.tsx +0 -124
  59. package/src/admin/ui/components/ui/dialog.tsx +0 -79
  60. package/src/admin/ui/components/ui/group.tsx +0 -20
  61. package/src/admin/ui/components/ui/index.ts +0 -17
  62. package/src/admin/ui/components/ui/input.tsx +0 -12
  63. package/src/admin/ui/components/ui/join.tsx +0 -53
  64. package/src/admin/ui/components/ui/label.tsx +0 -11
  65. package/src/admin/ui/components/ui/radio-group.tsx +0 -75
  66. package/src/admin/ui/components/ui/relationship-detail-sheet.tsx +0 -122
  67. package/src/admin/ui/components/ui/relationship.tsx +0 -58
  68. package/src/admin/ui/components/ui/scroll-area.tsx +0 -19
  69. package/src/admin/ui/components/ui/select.tsx +0 -187
  70. package/src/admin/ui/components/ui/separator.tsx +0 -21
  71. package/src/admin/ui/components/ui/sheet.tsx +0 -106
  72. package/src/admin/ui/components/ui/tabs.tsx +0 -116
  73. package/src/admin/ui/components/ui/utils.ts +0 -3
  74. package/src/admin/ui/hooks/use-debounce.ts +0 -15
  75. package/src/admin/ui/styles/_locale-switcher.scss +0 -33
  76. package/src/admin/ui/styles/accordion.scss +0 -60
  77. package/src/admin/ui/styles/animations.scss +0 -41
  78. package/src/admin/ui/styles/asset-manager.scss +0 -547
  79. package/src/admin/ui/styles/badge.scss +0 -13
  80. package/src/admin/ui/styles/base.scss +0 -22
  81. package/src/admin/ui/styles/button.scss +0 -161
  82. package/src/admin/ui/styles/card.scss +0 -13
  83. package/src/admin/ui/styles/collapsible.scss +0 -75
  84. package/src/admin/ui/styles/data-detail.scss +0 -92
  85. package/src/admin/ui/styles/dialog.scss +0 -102
  86. package/src/admin/ui/styles/empty-state.scss +0 -22
  87. package/src/admin/ui/styles/group.scss +0 -19
  88. package/src/admin/ui/styles/index.scss +0 -33
  89. package/src/admin/ui/styles/input.scss +0 -80
  90. package/src/admin/ui/styles/label.scss +0 -12
  91. package/src/admin/ui/styles/layout.scss +0 -56
  92. package/src/admin/ui/styles/lexical.scss +0 -469
  93. package/src/admin/ui/styles/loading.scss +0 -102
  94. package/src/admin/ui/styles/media-registry.scss +0 -597
  95. package/src/admin/ui/styles/pagination.scss +0 -20
  96. package/src/admin/ui/styles/radio-group.scss +0 -66
  97. package/src/admin/ui/styles/row.scss +0 -17
  98. package/src/admin/ui/styles/scrollbar.scss +0 -36
  99. package/src/admin/ui/styles/select.scss +0 -121
  100. package/src/admin/ui/styles/separator.scss +0 -14
  101. package/src/admin/ui/styles/sheet.scss +0 -152
  102. package/src/admin/ui/styles/sidebar.scss +0 -148
  103. package/src/admin/ui/styles/switch.scss +0 -59
  104. package/src/admin/ui/styles/table.scss +0 -207
  105. package/src/admin/ui/styles/tabs.scss +0 -62
  106. package/src/admin/ui/styles/toast.scss +0 -45
  107. package/src/admin/ui/styles/variables.scss +0 -24
  108. package/src/admin/ui/views/collection-list-view.tsx +0 -720
  109. package/src/admin/ui/views/dashboard-view.tsx +0 -263
  110. package/src/admin/ui/views/document-edit-view.tsx +0 -384
  111. package/src/admin/ui/views/global-edit-view.tsx +0 -226
  112. package/src/admin/ui/views/init-view.tsx +0 -182
  113. package/src/admin/ui/views/login-view.tsx +0 -123
  114. package/src/admin/ui/views/media-registry-view.tsx +0 -1104
  115. package/src/admin/ui/views/settings-view.tsx +0 -729
  116. package/src/admin/webcomponent.tsx +0 -15
  117. package/src/auth/index.ts +0 -194
  118. package/src/auth/migrations.ts +0 -87
  119. package/src/auth/premissions.ts +0 -46
  120. package/src/cli/commands/generate-types.ts +0 -116
  121. package/src/cli/commands/init.ts +0 -95
  122. package/src/cli/commands/migrate-commands.ts +0 -160
  123. package/src/cli/commands/seed-command.ts +0 -11
  124. package/src/cli/d1-mock.ts +0 -101
  125. package/src/cli/index.test.ts +0 -84
  126. package/src/cli/index.ts +0 -183
  127. package/src/cli/r2-mock.ts +0 -217
  128. package/src/cli/seeding.ts +0 -409
  129. package/src/client.ts +0 -181
  130. package/src/config-utils.ts +0 -102
  131. package/src/config.ts +0 -49
  132. package/src/db/adapter.ts +0 -53
  133. package/src/db/better-sqlite.ts +0 -630
  134. package/src/db/bun-sqlite.ts +0 -646
  135. package/src/db/d1.ts +0 -711
  136. package/src/db/index.ts +0 -2
  137. package/src/db/kysely/data-mapper.ts +0 -142
  138. package/src/db/kysely/field-mapper.ts +0 -148
  139. package/src/db/kysely/migration-generator.ts +0 -223
  140. package/src/db/kysely/query-builder.ts +0 -92
  141. package/src/db/kysely/schema-builder.ts +0 -439
  142. package/src/db/kysely/sql-utils.ts +0 -13
  143. package/src/db/migration.ts +0 -40
  144. package/src/db/postgres.ts +0 -621
  145. package/src/db/sqlite.ts +0 -658
  146. package/src/db/system-schema.ts +0 -121
  147. package/src/index.ts +0 -11
  148. package/src/runtimes/README.md +0 -59
  149. package/src/runtimes/bun.ts +0 -49
  150. package/src/runtimes/cloudflare-workers.ts +0 -38
  151. package/src/runtimes/next.ts +0 -26
  152. package/src/runtimes/node.ts +0 -52
  153. package/src/schema/collection.ts +0 -184
  154. package/src/schema/fields/base.ts +0 -164
  155. package/src/schema/fields/index.ts +0 -427
  156. package/src/schema/global.ts +0 -145
  157. package/src/schema/index.ts +0 -4
  158. package/src/schema/infer.ts +0 -72
  159. package/src/server/admin-router.ts +0 -20
  160. package/src/server/admin.ts +0 -142
  161. package/src/server/assets.ts +0 -306
  162. package/src/server/collection-router.ts +0 -55
  163. package/src/server/handlers.ts +0 -722
  164. package/src/server/middlewares/admin.ts +0 -27
  165. package/src/server/middlewares/auth.ts +0 -89
  166. package/src/server/middlewares/context.ts +0 -17
  167. package/src/server/middlewares/cors.ts +0 -24
  168. package/src/server/middlewares/database-init.ts +0 -74
  169. package/src/server/middlewares/rate-limit.ts +0 -71
  170. package/src/server/router.ts +0 -47
  171. package/src/server/setup-middlewares.ts +0 -58
  172. package/src/server/system-router.ts +0 -35
  173. package/src/server.ts +0 -9
  174. package/src/storage/adapters/cloudflare-r2.ts +0 -136
  175. package/src/storage/adapters/local.ts +0 -146
  176. package/src/storage/adapters/s3.ts +0 -186
  177. package/src/storage/errors.ts +0 -46
  178. package/src/storage/index.ts +0 -6
  179. package/src/storage/types.ts +0 -39
  180. package/src/types.ts +0 -605
  181. package/src/utils/lexical.ts +0 -37
  182. package/src/utils/logger.ts +0 -73
  183. package/src/validation.ts +0 -429
  184. package/src/validator.ts +0 -179
  185. package/test/admin-custom-field.test.ts +0 -162
  186. package/test/admin-react-field.test.tsx +0 -134
  187. package/test/api-features.test.ts +0 -78
  188. package/test/api.test.ts +0 -178
  189. package/test/auth.test.ts +0 -62
  190. package/test/cli-integration.test.ts +0 -148
  191. package/test/cli.test.ts +0 -25
  192. package/test/db/postgres.test.ts +0 -95
  193. package/test/db/sqlite-filter.test.ts +0 -53
  194. package/test/db/sqlite.test.ts +0 -82
  195. package/test/engine-features.test.ts +0 -79
  196. package/test/globals.test.ts +0 -74
  197. package/test/integration-tmp/db-app/opacacms.config.ts +0 -15
  198. package/test/integration-tmp/my-sqlite-app/opacacms.config.ts +0 -25
  199. package/test/integration-tmp/my-test-app/index.ts +0 -8
  200. package/test/integration-tmp/my-test-app/opacacms.config.ts +0 -16
  201. package/test/integration-tmp/my-test-app/package.json +0 -12
  202. package/test/populate.test.ts +0 -79
  203. package/test/runtimes.test.ts +0 -43
  204. package/test/schema-builder.test.ts +0 -107
  205. package/test/schema-features.test.ts +0 -63
  206. package/test/seeding.test.ts +0 -68
  207. package/test/storage/local.test.ts +0 -72
  208. package/test/storage/s3.test.ts +0 -60
  209. package/test/structural-data.test.ts +0 -100
  210. package/test/test-setup.ts +0 -11
  211. package/test/validation.test.ts +0 -162
  212. package/tsconfig.json +0 -42
@@ -1,72 +0,0 @@
1
- import { CheckCircle, Info, X, XCircle } from "lucide-react";
2
- import { useEffect, useState } from "react";
3
- import type { ToastItem, ToastType } from "../../stores/ui";
4
-
5
- interface ToastProps extends ToastItem {
6
- onClear: (id: string) => void;
7
- }
8
-
9
- export function Toast({ id, message, type, onClear }: ToastProps) {
10
- const [isExiting, setIsExiting] = useState(false);
11
-
12
- useEffect(() => {
13
- const timer = setTimeout(() => {
14
- setIsExiting(true);
15
- setTimeout(() => onClear(id), 200);
16
- }, 4000);
17
- return () => clearTimeout(timer);
18
- }, [id, onClear]);
19
-
20
- const Icon = type === "success" ? CheckCircle : type === "error" ? XCircle : Info;
21
- const iconColor =
22
- type === "success"
23
- ? "var(--opaca-success)"
24
- : type === "error"
25
- ? "var(--opaca-error)"
26
- : "var(--opaca-accent)";
27
-
28
- return (
29
- <div className={`opaca-toast opaca-toast-${type} ${isExiting ? "exit" : ""}`}>
30
- <Icon size={18} style={{ color: iconColor }} />
31
- <span className="opaca-toast-message">{message}</span>
32
- <button
33
- type="button"
34
- onClick={() => {
35
- setIsExiting(true);
36
- setTimeout(() => onClear(id), 200);
37
- }}
38
- style={{
39
- background: "none",
40
- border: "none",
41
- padding: "4px",
42
- marginLeft: "auto",
43
- cursor: "pointer",
44
- color: "var(--opaca-text-dim)",
45
- display: "flex",
46
- alignItems: "center",
47
- justifyContent: "center",
48
- }}
49
- >
50
- <X size={14} />
51
- </button>
52
- </div>
53
- );
54
- }
55
-
56
- export function ToastContainer({
57
- toasts,
58
- onClear,
59
- }: {
60
- toasts: ToastItem[];
61
- onClear: (id: string) => void;
62
- }) {
63
- if (toasts.length === 0) return null;
64
-
65
- return (
66
- <div className="opaca-toast-container">
67
- {toasts.map((toast) => (
68
- <Toast key={toast.id} {...toast} onClear={onClear} />
69
- ))}
70
- </div>
71
- );
72
- }
@@ -1,51 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
- import "../../styles/accordion.scss";
4
-
5
- interface AccordionProps {
6
- title: string;
7
- children: React.ReactNode;
8
- defaultOpen?: boolean;
9
- className?: string;
10
- isCollapsed?: boolean;
11
- }
12
-
13
- export const Accordion = ({
14
- title,
15
- children,
16
- defaultOpen = true,
17
- className,
18
- isCollapsed = false,
19
- }: AccordionProps) => {
20
- const [isOpen, setIsOpen] = React.useState(defaultOpen);
21
-
22
- return (
23
- <div className={cn("opaca-sidebar-accordion", className)}>
24
- <button
25
- type="button"
26
- className="opaca-sidebar-accordion-trigger"
27
- onClick={() => setIsOpen(!isOpen)}
28
- aria-expanded={isOpen}
29
- style={{
30
- opacity: isCollapsed ? 0 : 1,
31
- pointerEvents: isCollapsed ? "none" : "auto",
32
- }}
33
- title={title}
34
- >
35
- <span className="opaca-sidebar-accordion-title">{title}</span>
36
- <svg
37
- className="opaca-sidebar-accordion-icon"
38
- viewBox="0 0 24 24"
39
- xmlns="http://www.w3.org/2000/svg"
40
- aria-hidden="true"
41
- >
42
- <title>Toggle accordion</title>
43
- <polyline points="6 9 12 15 18 9"></polyline>
44
- </svg>
45
- </button>
46
- <div className="opaca-sidebar-accordion-content" data-state={isOpen ? "open" : "closed"}>
47
- <div className="opaca-sidebar-accordion-content-inner">{children}</div>
48
- </div>
49
- </div>
50
- );
51
- };
@@ -1,98 +0,0 @@
1
- import { Children, cloneElement, isValidElement, type ReactNode, useState } from "react";
2
- import { Button } from "./button";
3
- import {
4
- Dialog,
5
- DialogContent,
6
- DialogDescription,
7
- DialogFooter,
8
- DialogHeader,
9
- DialogTitle,
10
- } from "./dialog";
11
-
12
- export interface AlertDialogProps {
13
- children?: ReactNode;
14
- trigger?: ReactNode;
15
- title: string;
16
- description: string;
17
- onConfirm: () => void;
18
- confirmText?: string;
19
- cancelText?: string;
20
- variant?: "default" | "destructive";
21
- }
22
-
23
- export function AlertDialog({
24
- children,
25
- trigger,
26
- title,
27
- description,
28
- onConfirm,
29
- confirmText = "Continue",
30
- cancelText = "Cancel",
31
- variant = "default",
32
- }: AlertDialogProps) {
33
- const [open, setOpen] = useState(false);
34
-
35
- const handleConfirm = () => {
36
- onConfirm();
37
- setOpen(false);
38
- };
39
-
40
- const triggerElement = trigger || children;
41
-
42
- const renderTrigger = () => {
43
- if (Children.count(triggerElement) === 1 && isValidElement(triggerElement)) {
44
- const element = triggerElement as React.ReactElement<{
45
- onClick?: (e: React.MouseEvent) => void;
46
- }>;
47
- return cloneElement(element, {
48
- onClick: (e: React.MouseEvent) => {
49
- const originalOnClick = element.props.onClick;
50
- if (originalOnClick) originalOnClick(e);
51
- setOpen(true);
52
- },
53
- });
54
- }
55
-
56
- return (
57
- <span
58
- role="button"
59
- tabIndex={0}
60
- onClick={() => setOpen(true)}
61
- onKeyDown={(e) => {
62
- if (e.key === "Enter" || e.key === " ") {
63
- e.preventDefault();
64
- setOpen(true);
65
- }
66
- }}
67
- style={{
68
- display: "inline-block",
69
- cursor: "pointer",
70
- }}
71
- >
72
- {triggerElement}
73
- </span>
74
- );
75
- };
76
-
77
- return (
78
- <>
79
- {renderTrigger()}
80
- <Dialog open={open} onOpenChange={setOpen}>
81
- <DialogContent className="opaca-dialog-max-w">
82
- <DialogHeader>
83
- <DialogTitle>{title}</DialogTitle>
84
- <DialogDescription>{description}</DialogDescription>
85
- </DialogHeader>
86
- <DialogFooter>
87
- <Button variant="outline" onClick={() => setOpen(false)}>
88
- {cancelText}
89
- </Button>
90
- <Button variant={variant} onClick={handleConfirm}>
91
- {confirmText}
92
- </Button>
93
- </DialogFooter>
94
- </DialogContent>
95
- </Dialog>
96
- </>
97
- );
98
- }
@@ -1,32 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
-
4
- export interface BlocksProps extends React.HTMLAttributes<HTMLDivElement> {
5
- label?: string;
6
- }
7
-
8
- export const Blocks = React.forwardRef<HTMLDivElement, BlocksProps>(
9
- ({ className, label, children, ...props }, ref) => {
10
- return (
11
- <div ref={ref} className={cn("opaca-ui-blocks", className)} {...props}>
12
- {label && (
13
- <div
14
- style={{
15
- marginBottom: "1.25rem",
16
- fontSize: "0.75rem",
17
- fontWeight: 500,
18
- color: "var(--opaca-text-muted)",
19
- textTransform: "uppercase",
20
- letterSpacing: "0.03em",
21
- }}
22
- >
23
- {label}
24
- </div>
25
- )}
26
- <div style={{ display: "flex", flexDirection: "column", gap: "1.5rem" }}>{children}</div>
27
- </div>
28
- );
29
- },
30
- );
31
-
32
- Blocks.displayName = "Blocks";
@@ -1,59 +0,0 @@
1
- import { ChevronRight, Home } from 'lucide-react';
2
- import React from 'react';
3
- import { Link } from '../link';
4
-
5
- export interface BreadcrumbItem {
6
- label: string;
7
- href?: string;
8
- }
9
-
10
- export function Breadcrumbs({ items }: { items: BreadcrumbItem[] }) {
11
- return (
12
- <nav
13
- aria-label="Breadcrumb"
14
- style={{
15
- display: 'flex',
16
- alignItems: 'center',
17
- gap: '0.5rem',
18
- marginBottom: '1.5rem',
19
- fontSize: '0.8125rem',
20
- color: 'var(--opaca-text-dim)',
21
- }}
22
- >
23
- <Link
24
- href="/admin"
25
- style={{
26
- display: 'flex',
27
- alignItems: 'center',
28
- color: 'inherit',
29
- textDecoration: 'none',
30
- transition: 'color var(--opaca-transition)',
31
- }}
32
- >
33
- <Home size={14} />
34
- </Link>
35
-
36
- {items.map((item, index) => (
37
- <React.Fragment key={item.label || index}>
38
- <ChevronRight size={12} style={{ opacity: 0.5 }} />
39
- {item.href ? (
40
- <Link
41
- href={item.href}
42
- style={{
43
- color: 'inherit',
44
- textDecoration: 'none',
45
- transition: 'color var(--opaca-transition)',
46
- }}
47
- >
48
- {item.label}
49
- </Link>
50
- ) : (
51
- <span style={{ color: 'var(--opaca-text)', fontWeight: '500' }}>
52
- {item.label}
53
- </span>
54
- )}
55
- </React.Fragment>
56
- ))}
57
- </nav>
58
- );
59
- }
@@ -1,26 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
- import "../../styles/button.scss";
4
-
5
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6
- variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
7
- size?: "default" | "sm" | "lg" | "icon";
8
- }
9
-
10
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
11
- ({ className, variant = "default", size = "default", ...props }, ref) => {
12
- return (
13
- <button
14
- ref={ref}
15
- className={cn(
16
- "opaca-ui-btn",
17
- `opaca-ui-btn-${variant}`,
18
- `opaca-ui-btn-size-${size}`,
19
- className,
20
- )}
21
- {...props}
22
- />
23
- );
24
- },
25
- );
26
- Button.displayName = "Button";
@@ -1,124 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
- import "../../styles/collapsible.scss";
4
-
5
- interface CollapsibleContextValue {
6
- open?: boolean;
7
- onOpenChange?: (open: boolean) => void;
8
- disabled?: boolean;
9
- }
10
-
11
- const CollapsibleContext = React.createContext<CollapsibleContextValue>({});
12
-
13
- export interface CollapsibleProps extends React.HTMLAttributes<HTMLDivElement> {
14
- open?: boolean;
15
- defaultOpen?: boolean;
16
- onOpenChange?: (open: boolean) => void;
17
- disabled?: boolean;
18
- }
19
-
20
- export const Collapsible = React.forwardRef<HTMLDivElement, CollapsibleProps>(
21
- ({ className, open, defaultOpen, onOpenChange, disabled, children, ...props }, ref) => {
22
- const [uncontrolledOpen, setUncontrolledOpen] = React.useState<boolean>(defaultOpen ?? false);
23
-
24
- const isControlled = open !== undefined;
25
- const currentOpen = isControlled ? open : uncontrolledOpen;
26
-
27
- const handleOpenChange = React.useCallback(
28
- (newOpen: boolean) => {
29
- if (!isControlled) {
30
- setUncontrolledOpen(newOpen);
31
- }
32
- if (onOpenChange) {
33
- onOpenChange(newOpen);
34
- }
35
- },
36
- [isControlled, onOpenChange],
37
- );
38
-
39
- const contextValue = React.useMemo(
40
- () => ({ open: currentOpen, onOpenChange: handleOpenChange, disabled }),
41
- [currentOpen, handleOpenChange, disabled],
42
- );
43
-
44
- return (
45
- <CollapsibleContext.Provider value={contextValue}>
46
- <div ref={ref} className={cn("opaca-ui-collapsible", className)} {...props}>
47
- {children}
48
- </div>
49
- </CollapsibleContext.Provider>
50
- );
51
- },
52
- );
53
- Collapsible.displayName = "Collapsible";
54
-
55
- export interface CollapsibleTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
56
- asChild?: boolean;
57
- }
58
-
59
- export const CollapsibleTrigger = React.forwardRef<HTMLButtonElement, CollapsibleTriggerProps>(
60
- ({ className, disabled, children, ...props }, ref) => {
61
- const context = React.useContext(CollapsibleContext);
62
- const isDisabled = disabled || context.disabled;
63
- const isOpen = context.open;
64
-
65
- const handleClick = React.useCallback(
66
- (event: React.MouseEvent<HTMLButtonElement>) => {
67
- if (!isDisabled && context.onOpenChange) {
68
- context.onOpenChange(!isOpen);
69
- }
70
- if (props.onClick) {
71
- props.onClick(event);
72
- }
73
- },
74
- [isDisabled, isOpen, context.onOpenChange, props.onClick],
75
- );
76
-
77
- return (
78
- <button
79
- type="button"
80
- aria-expanded={isOpen}
81
- disabled={isDisabled}
82
- onClick={handleClick}
83
- ref={ref}
84
- className={cn("opaca-ui-collapsible-trigger", className)}
85
- {...props}
86
- >
87
- {children}
88
- <svg
89
- className="opaca-ui-collapsible-icon"
90
- viewBox="0 0 24 24"
91
- xmlns="http://www.w3.org/2000/svg"
92
- >
93
- <polyline points="6 9 12 15 18 9"></polyline>
94
- </svg>
95
- </button>
96
- );
97
- },
98
- );
99
- CollapsibleTrigger.displayName = "CollapsibleTrigger";
100
-
101
- export interface CollapsibleContentProps extends React.HTMLAttributes<HTMLDivElement> {}
102
-
103
- export const CollapsibleContent = React.forwardRef<HTMLDivElement, CollapsibleContentProps>(
104
- ({ className, children, ...props }, ref) => {
105
- const context = React.useContext(CollapsibleContext);
106
- const isOpen = context.open;
107
-
108
- if (!isOpen) {
109
- return null;
110
- }
111
-
112
- return (
113
- <div
114
- data-state={isOpen ? "open" : "closed"}
115
- ref={ref}
116
- className={cn("opaca-ui-collapsible-content", className)}
117
- {...props}
118
- >
119
- {children}
120
- </div>
121
- );
122
- },
123
- );
124
- CollapsibleContent.displayName = "CollapsibleContent";
@@ -1,79 +0,0 @@
1
- import type * as React from "react";
2
- import { createPortal } from "react-dom";
3
- import { cn } from "./utils";
4
- import "../../styles/dialog.scss";
5
-
6
- export function Dialog({
7
- open,
8
- onOpenChange,
9
- children,
10
- }: {
11
- open: boolean;
12
- onOpenChange: (open: boolean) => void;
13
- children: React.ReactNode;
14
- }) {
15
- if (!open) return null;
16
- return createPortal(
17
- <div className="opaca-ui-dialog-portal">
18
- <button
19
- type="button"
20
- className="opaca-ui-dialog-overlay"
21
- onClick={() => onOpenChange(false)}
22
- tabIndex={-1}
23
- aria-hidden="true"
24
- />
25
- <div className="opaca-ui-dialog-wrapper">{children}</div>
26
- </div>,
27
- document.body,
28
- );
29
- }
30
-
31
- export function DialogContent({
32
- children,
33
- className,
34
- }: {
35
- children: React.ReactNode;
36
- className?: string;
37
- }) {
38
- return <div className={cn("opaca-ui-dialog-content", className)}>{children}</div>;
39
- }
40
-
41
- export function DialogHeader({
42
- children,
43
- className,
44
- }: {
45
- children: React.ReactNode;
46
- className?: string;
47
- }) {
48
- return <div className={cn("opaca-ui-dialog-header", className)}>{children}</div>;
49
- }
50
-
51
- export function DialogTitle({
52
- children,
53
- className,
54
- }: {
55
- children: React.ReactNode;
56
- className?: string;
57
- }) {
58
- return <h2 className={cn("opaca-ui-dialog-title", className)}>{children}</h2>;
59
- }
60
-
61
- export function DialogDescription({
62
- children,
63
- className,
64
- }: {
65
- children: React.ReactNode;
66
- className?: string;
67
- }) {
68
- return <p className={cn("opaca-ui-dialog-description", className)}>{children}</p>;
69
- }
70
-
71
- export function DialogFooter({
72
- children,
73
- className,
74
- }: {
75
- children: React.ReactNode;
76
- className?: string;
77
- }) {
78
- return <div className={cn("opaca-ui-dialog-footer", className)}>{children}</div>;
79
- }
@@ -1,20 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
- import "../../styles/group.scss";
4
-
5
- export interface GroupProps extends React.HTMLAttributes<HTMLDivElement> {
6
- label?: string;
7
- }
8
-
9
- export const Group = React.forwardRef<HTMLDivElement, GroupProps>(
10
- ({ className, label, children, ...props }, ref) => {
11
- return (
12
- <div ref={ref} className={cn("opaca-ui-group", className)} {...props}>
13
- {label && <h3 className="opaca-ui-group-header">{label}</h3>}
14
- {children}
15
- </div>
16
- );
17
- },
18
- );
19
-
20
- Group.displayName = "Group";
@@ -1,17 +0,0 @@
1
- export * from "./accordion";
2
- export * from "./blocks";
3
- export * from "./button";
4
- export * from "./collapsible";
5
- export * from "./dialog";
6
- export * from "./group";
7
- export * from "./input";
8
- export * from "./join";
9
- export * from "./label";
10
- export * from "./radio-group";
11
- export * from "./relationship";
12
- export * from "./scroll-area";
13
- export * from "./select";
14
- export * from "./separator";
15
- export * from "./sheet";
16
- export * from "./tabs";
17
- export * from "./utils";
@@ -1,12 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
- import "../../styles/input.scss";
4
-
5
- export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
6
-
7
- export const Input = React.forwardRef<HTMLInputElement, InputProps>(
8
- ({ className, type, ...props }, ref) => {
9
- return <input type={type} className={cn("opaca-ui-input", className)} ref={ref} {...props} />;
10
- },
11
- );
12
- Input.displayName = "Input";
@@ -1,53 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
-
4
- export interface JoinProps extends React.HTMLAttributes<HTMLDivElement> {
5
- label?: string;
6
- collection: string;
7
- on?: string;
8
- }
9
-
10
- export const Join = React.forwardRef<HTMLDivElement, JoinProps>(
11
- ({ className, label, collection, children, ...props }, ref) => {
12
- return (
13
- <div ref={ref} className={cn("opaca-ui-join", className)} {...props}>
14
- {label && (
15
- <div
16
- style={{
17
- display: "block",
18
- marginBottom: "0.5rem",
19
- fontSize: "0.75rem",
20
- fontWeight: 500,
21
- color: "var(--opaca-text-muted)",
22
- textTransform: "uppercase",
23
- letterSpacing: "0.03em",
24
- }}
25
- >
26
- {label}
27
- </div>
28
- )}
29
- {/* Placeholder for complex virtual data fetch UI */}
30
- <div
31
- style={{
32
- display: "flex",
33
- alignItems: "center",
34
- justifyContent: "space-between",
35
- padding: "0.75rem",
36
- border: "1px solid var(--opaca-border)",
37
- borderRadius: "var(--opaca-radius)",
38
- backgroundColor: "rgba(255, 255, 255, 0.02)",
39
- color: "var(--opaca-text-dim)",
40
- fontSize: "0.8125rem",
41
- }}
42
- >
43
- <span>
44
- Virtually joined from: <strong>{collection}</strong>
45
- </span>
46
- <span style={{ fontSize: "0.7rem", opacity: 0.5 }}>On: {props.on || "dynamic"}</span>
47
- </div>
48
- </div>
49
- );
50
- },
51
- );
52
-
53
- Join.displayName = "Join";
@@ -1,11 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "./utils";
3
- import "../../styles/label.scss";
4
-
5
- export const Label = React.forwardRef<
6
- HTMLLabelElement,
7
- React.LabelHTMLAttributes<HTMLLabelElement>
8
- >(({ className, ...props }, ref) => (
9
- <label ref={ref} className={cn("opaca-ui-label", className)} {...props} />
10
- ));
11
- Label.displayName = "Label";