@orangecheck/app-design 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +4 -3
  2. package/dist/brand.js.map +1 -1
  3. package/dist/brand.mjs.map +1 -1
  4. package/dist/chrome.d.mts +49 -15
  5. package/dist/chrome.d.ts +49 -15
  6. package/dist/chrome.js +619 -55
  7. package/dist/chrome.js.map +1 -1
  8. package/dist/chrome.mjs +617 -57
  9. package/dist/chrome.mjs.map +1 -1
  10. package/dist/components.d.mts +231 -1
  11. package/dist/components.d.ts +231 -1
  12. package/dist/components.js +1187 -2
  13. package/dist/components.js.map +1 -1
  14. package/dist/components.mjs +1162 -5
  15. package/dist/components.mjs.map +1 -1
  16. package/dist/family.d.mts +2 -0
  17. package/dist/family.d.ts +2 -0
  18. package/dist/family.js +167 -0
  19. package/dist/family.js.map +1 -0
  20. package/dist/family.mjs +154 -0
  21. package/dist/family.mjs.map +1 -0
  22. package/dist/index-C6hDqOOS.d.mts +58 -0
  23. package/dist/index-C6hDqOOS.d.ts +58 -0
  24. package/dist/index.d.mts +5 -3
  25. package/dist/index.d.ts +5 -3
  26. package/dist/index.js +1619 -132
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +1575 -134
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/primitives.d.mts +52 -1
  31. package/dist/primitives.d.ts +52 -1
  32. package/dist/primitives.js +280 -3
  33. package/dist/primitives.js.map +1 -1
  34. package/dist/primitives.mjs +271 -6
  35. package/dist/primitives.mjs.map +1 -1
  36. package/dist/status-badge-BEOxeY9j.d.ts +13 -0
  37. package/dist/status-badge-DRwGhB0h.d.mts +13 -0
  38. package/dist/styles/fonts.css +1 -1
  39. package/dist/styles/tokens.css +4 -2
  40. package/dist/tokens.d.mts +3 -1
  41. package/dist/tokens.d.ts +3 -1
  42. package/dist/tokens.js +5 -3
  43. package/dist/tokens.js.map +1 -1
  44. package/dist/tokens.mjs +5 -3
  45. package/dist/tokens.mjs.map +1 -1
  46. package/package.json +9 -2
@@ -1,6 +1,6 @@
1
1
  import { Slot } from '@radix-ui/react-slot';
2
2
  import { cva } from 'class-variance-authority';
3
- import { forwardRef, createContext, useContext, useRef, useState, useCallback, useEffect, useId, isValidElement, cloneElement } from 'react';
3
+ import { forwardRef, createContext, useContext, useRef, useState, useCallback, useEffect, useId, isValidElement, cloneElement, useSyncExternalStore, useMemo } from 'react';
4
4
  import { clsx } from 'clsx';
5
5
  import { extendTailwindMerge } from 'tailwind-merge';
6
6
  import * as SeparatorPrimitive from '@radix-ui/react-separator';
@@ -12,12 +12,13 @@ import * as LabelPrimitive from '@radix-ui/react-label';
12
12
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
13
13
  import * as SelectPrimitive from '@radix-ui/react-select';
14
14
  import * as SwitchPrimitive from '@radix-ui/react-switch';
15
- import { ChevronDown, Check, X } from 'lucide-react';
15
+ import { ChevronDown, Check, X, ChevronLeft, ChevronRight } from 'lucide-react';
16
16
  import * as DialogPrimitive from '@radix-ui/react-dialog';
17
17
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
18
18
  import * as PopoverPrimitive from '@radix-ui/react-popover';
19
19
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
20
20
  import * as TabsPrimitive from '@radix-ui/react-tabs';
21
+ import QRCode from 'qrcode';
21
22
 
22
23
  var merge = extendTailwindMerge({
23
24
  extend: {
@@ -206,13 +207,13 @@ var badgeVariants = cva(
206
207
  tone: {
207
208
  neutral: "border-border bg-card text-foreground",
208
209
  muted: "border-transparent bg-muted text-muted-foreground",
209
- brand: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_12%,var(--card))] text-oca-accent-ink",
210
+ brand: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_9%,var(--card))] text-oca-accent-ink",
210
211
  danger: "border-transparent bg-[color-mix(in_oklab,var(--destructive)_12%,var(--card))] text-destructive",
211
212
  // A good state is a neutral pill with a green dot, not a green pill.
212
213
  success: GOOD_DOT,
213
214
  // Aliases kept so older call sites keep compiling: attention is
214
215
  // the brand accent, information is neutral.
215
- warning: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_12%,var(--card))] text-oca-accent-ink",
216
+ warning: "border-transparent bg-[color-mix(in_oklab,var(--oca-orange)_9%,var(--card))] text-oca-accent-ink",
216
217
  info: "border-border bg-card text-foreground",
217
218
  // Inverted in dark mode, where ink would sit on a card of nearly its own colour.
218
219
  ink: "border-transparent bg-oca-ink text-oca-ink-foreground dark:bg-foreground dark:text-background",
@@ -250,6 +251,30 @@ function StatusPill({
250
251
  }
251
252
  );
252
253
  }
254
+ var WORD = {
255
+ live: "Live",
256
+ testing: "In testing",
257
+ "not-yet": "Not yet",
258
+ paused: "Paused",
259
+ good: "Good",
260
+ caution: "Caution",
261
+ refused: "Refused"
262
+ };
263
+ var TONE = {
264
+ live: "success",
265
+ testing: "brand",
266
+ "not-yet": "muted",
267
+ paused: "muted",
268
+ good: "success",
269
+ caution: "brand",
270
+ refused: "danger"
271
+ };
272
+ function statusWord(status) {
273
+ return WORD[status];
274
+ }
275
+ function StatusBadge({ status, className, ...props }) {
276
+ return /* @__PURE__ */ jsx(Badge, { tone: TONE[status], "data-status": status, className: cn(className), ...props, children: WORD[status] });
277
+ }
253
278
  var cardVariants = cva("relative rounded-[1.25rem] border", {
254
279
  variants: {
255
280
  tone: {
@@ -433,7 +458,8 @@ var Switch = forwardRef(function Switch2({ className, ...props }, ref) {
433
458
  className: cn(
434
459
  "peer inline-flex h-6 w-11 shrink-0 items-center rounded-full border border-transparent bg-input p-0.5",
435
460
  "outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
436
- "data-[state=checked]:bg-brand disabled:cursor-not-allowed disabled:opacity-50",
461
+ // The deep orange: the track is the state, so it must clear 3:1 on a card.
462
+ "data-[state=checked]:bg-oca-orange-deep disabled:cursor-not-allowed disabled:opacity-50",
437
463
  className
438
464
  ),
439
465
  ...props,
@@ -725,7 +751,246 @@ var TabsTrigger = forwardRef(function TabsTrigger2({ className, ...props }, ref)
725
751
  var TabsContent = forwardRef(function TabsContent2({ className, ...props }, ref) {
726
752
  return /* @__PURE__ */ jsx(TabsPrimitive.Content, { ref, className: cn("mt-6 outline-none", className), ...props });
727
753
  });
754
+ var GroupName = createContext(void 0);
755
+ function Accordion({
756
+ single = false,
757
+ children,
758
+ className
759
+ }) {
760
+ const name = useId();
761
+ return /* @__PURE__ */ jsx(GroupName.Provider, { value: single ? name : void 0, children: /* @__PURE__ */ jsx("div", { className: cn("divide-y divide-border overflow-hidden rounded-[1.25rem] border border-border bg-card", className), children }) });
762
+ }
763
+ function AccordionItem({
764
+ title,
765
+ children,
766
+ defaultOpen = false,
767
+ id,
768
+ className
769
+ }) {
770
+ const name = useContext(GroupName);
771
+ return /* @__PURE__ */ jsxs("details", { id, name, open: defaultOpen || void 0, className: cn("group scroll-mt-24", className), children: [
772
+ /* @__PURE__ */ jsxs(
773
+ "summary",
774
+ {
775
+ className: cn(
776
+ "flex min-h-14 cursor-pointer list-none items-center justify-between gap-4 px-5 py-4 text-[1rem] font-medium leading-snug md:px-6",
777
+ // Inside the card's clip, so the ring is drawn within the row.
778
+ "transition-colors hover:bg-accent/60 focus-visible:outline-offset-[-2px] [&::-webkit-details-marker]:hidden"
779
+ ),
780
+ children: [
781
+ title,
782
+ /* @__PURE__ */ jsx(
783
+ ChevronDown,
784
+ {
785
+ "aria-hidden": true,
786
+ className: "size-4 shrink-0 text-muted-foreground transition-transform duration-200 group-open:rotate-180"
787
+ }
788
+ )
789
+ ]
790
+ }
791
+ ),
792
+ /* @__PURE__ */ jsx("div", { className: "oca-body px-5 pb-5 text-[0.9375rem] md:px-6 md:pb-6", children })
793
+ ] });
794
+ }
795
+ var queue = [];
796
+ var asked = 0;
797
+ var hosts = 0;
798
+ var listeners = /* @__PURE__ */ new Set();
799
+ var emit = () => listeners.forEach((l) => l());
800
+ function ask(item) {
801
+ queue = [...queue, { ...item, id: ++asked }];
802
+ emit();
803
+ }
804
+ function done() {
805
+ queue = queue.slice(1);
806
+ emit();
807
+ }
808
+ function confirm(options) {
809
+ if (hosts === 0) {
810
+ console.error("confirm(): no <DialogHost /> is mounted, so nothing was asked; treating it as no.");
811
+ return Promise.resolve(false);
812
+ }
813
+ return new Promise((resolve) => ask({ id: 0, kind: "confirm", options, settle: resolve }));
814
+ }
815
+ function prompt(options) {
816
+ if (hosts === 0) {
817
+ console.error("prompt(): no <DialogHost /> is mounted, so nothing was asked; treating it as cancelled.");
818
+ return Promise.resolve(null);
819
+ }
820
+ return new Promise((resolve) => ask({ id: 0, kind: "prompt", options, settle: resolve }));
821
+ }
822
+ function subscribe(listener) {
823
+ listeners.add(listener);
824
+ hosts++;
825
+ return () => {
826
+ listeners.delete(listener);
827
+ hosts--;
828
+ };
829
+ }
830
+ var current = () => queue[0] ?? null;
831
+ var none = () => null;
832
+ function PromptBody({ ask: a }) {
833
+ const o = a.options;
834
+ const [value, setValue] = useState(o.defaultValue ?? "");
835
+ const [error, setError] = useState(null);
836
+ const submit = (e) => {
837
+ e.preventDefault();
838
+ if (o.minLength && value.length < o.minLength) {
839
+ setError(`At least ${o.minLength} characters.`);
840
+ return;
841
+ }
842
+ const refused = o.validate?.(value);
843
+ if (refused) {
844
+ setError(refused);
845
+ return;
846
+ }
847
+ a.settle(value);
848
+ done();
849
+ };
850
+ return /* @__PURE__ */ jsxs("form", { onSubmit: submit, noValidate: true, children: [
851
+ /* @__PURE__ */ jsx(Field, { label: o.label ?? "Your answer", hint: o.hint, error, children: /* @__PURE__ */ jsx(
852
+ Input,
853
+ {
854
+ autoFocus: true,
855
+ type: o.secret ? "password" : "text",
856
+ autoComplete: o.secret ? "new-password" : "off",
857
+ placeholder: o.placeholder,
858
+ value,
859
+ onChange: (e) => {
860
+ setValue(e.target.value);
861
+ setError(null);
862
+ }
863
+ }
864
+ ) }),
865
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
866
+ /* @__PURE__ */ jsx(
867
+ Button,
868
+ {
869
+ variant: "secondary",
870
+ onClick: () => {
871
+ a.settle(null);
872
+ done();
873
+ },
874
+ children: o.cancelLabel ?? "Cancel"
875
+ }
876
+ ),
877
+ /* @__PURE__ */ jsx(Button, { type: "submit", variant: o.destructive ? "danger" : "primary", children: o.confirmLabel ?? "Continue" })
878
+ ] })
879
+ ] });
880
+ }
881
+ function DialogHost() {
882
+ const a = useSyncExternalStore(subscribe, current, none);
883
+ const cancel = () => {
884
+ if (!a) return;
885
+ if (a.kind === "confirm") a.settle(false);
886
+ else a.settle(null);
887
+ done();
888
+ };
889
+ return /* @__PURE__ */ jsx(Dialog, { open: a !== null, onOpenChange: (open) => open ? void 0 : cancel(), children: a ? /* @__PURE__ */ jsxs(
890
+ DialogContent,
891
+ {
892
+ role: "alertdialog",
893
+ ...a.options.message ? {} : { "aria-describedby": void 0 },
894
+ className: "w-[min(92vw,28rem)]",
895
+ children: [
896
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
897
+ /* @__PURE__ */ jsx(DialogTitle, { children: a.options.title }),
898
+ a.options.message ? /* @__PURE__ */ jsx(DialogDescription, { children: a.options.message }) : null
899
+ ] }),
900
+ a.kind === "prompt" ? (
901
+ // Keyed by the ask, so a second prompt starts empty.
902
+ /* @__PURE__ */ jsx(PromptBody, { ask: a }, a.id)
903
+ ) : /* @__PURE__ */ jsxs(DialogFooter, { children: [
904
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: cancel, children: a.options.cancelLabel ?? "Cancel" }),
905
+ /* @__PURE__ */ jsx(
906
+ Button,
907
+ {
908
+ variant: a.options.destructive ? "danger" : "primary",
909
+ onClick: () => {
910
+ a.settle(true);
911
+ done();
912
+ },
913
+ children: a.options.confirmLabel ?? "Continue"
914
+ }
915
+ )
916
+ ] })
917
+ ]
918
+ }
919
+ ) : null });
920
+ }
921
+ var N = new Intl.NumberFormat("en-US");
922
+ function Pagination({
923
+ pageIndex,
924
+ pageSize,
925
+ total,
926
+ onPage,
927
+ label = "Pages",
928
+ className
929
+ }) {
930
+ const pages = Math.max(1, Math.ceil(total / pageSize));
931
+ const index = Math.min(Math.max(0, pageIndex), pages - 1);
932
+ if (index !== pageIndex && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
933
+ console.warn(`Pagination: pageIndex ${pageIndex} is outside 0\u2013${pages - 1}; it counts from 0.`);
934
+ }
935
+ if (total <= pageSize) return null;
936
+ const from = index * pageSize + 1;
937
+ const to = Math.min(total, (index + 1) * pageSize);
938
+ return /* @__PURE__ */ jsxs("nav", { "aria-label": label, className: cn("flex items-center justify-between gap-4", className), children: [
939
+ /* @__PURE__ */ jsxs("p", { "aria-live": "polite", className: "oca-tabular text-[0.875rem] text-muted-foreground", children: [
940
+ N.format(from),
941
+ "\u2013",
942
+ N.format(to),
943
+ " of ",
944
+ N.format(total)
945
+ ] }),
946
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
947
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "sm", disabled: index === 0, onClick: () => onPage(index - 1), className: "pl-3", children: [
948
+ /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": true }),
949
+ "Previous"
950
+ ] }),
951
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "sm", disabled: index >= pages - 1, onClick: () => onPage(index + 1), className: "pr-3", children: [
952
+ "Next",
953
+ /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true })
954
+ ] })
955
+ ] })
956
+ ] });
957
+ }
958
+ function QrCode({
959
+ value,
960
+ size = 200,
961
+ label,
962
+ className
963
+ }) {
964
+ const matrix = useMemo(() => {
965
+ try {
966
+ return QRCode.create(value, { errorCorrectionLevel: "M" }).modules;
967
+ } catch {
968
+ return null;
969
+ }
970
+ }, [value]);
971
+ const path = useMemo(() => {
972
+ if (!matrix) return "";
973
+ let d = "";
974
+ for (let y = 0; y < matrix.size; y++) {
975
+ for (let x = 0; x < matrix.size; x++) {
976
+ if (matrix.get(y, x)) d += `M${x} ${y}h1v1h-1z`;
977
+ }
978
+ }
979
+ return d;
980
+ }, [matrix]);
981
+ const short = value.length > 28 ? `${value.slice(0, 28)}\u2026` : value;
982
+ return /* @__PURE__ */ jsx(
983
+ "div",
984
+ {
985
+ role: "img",
986
+ "aria-label": matrix ? label ?? `QR code for ${short}` : "Too long for a QR code",
987
+ className: cn("inline-block shrink-0 rounded-2xl bg-white p-3 ring-1 ring-border", className),
988
+ style: { width: size, height: size },
989
+ children: matrix ? /* @__PURE__ */ jsx("svg", { viewBox: `0 0 ${matrix.size} ${matrix.size}`, shapeRendering: "crispEdges", "aria-hidden": true, className: "block size-full", children: /* @__PURE__ */ jsx("path", { d: path, fill: "#0a0a0a" }) }) : /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "flex size-full items-center justify-center text-center text-[0.8125rem] text-[#54585e]", children: "Too long for a QR code" })
990
+ }
991
+ );
992
+ }
728
993
 
729
- export { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardNumeral, CardTitle, Checkbox, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Field, Input, Kbd, Label, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, RadioGroup, RadioGroupItem, ScrollFade, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetTitle, SheetTrigger, Skeleton, Spinner, StatusDot, StatusPill, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, cardVariants, cn, useTimedOut };
994
+ export { Accordion, AccordionItem, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardNumeral, CardTitle, Checkbox, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogHost, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Field, Input, Kbd, Label, Pagination, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, QrCode, RadioGroup, RadioGroupItem, ScrollFade, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetTitle, SheetTrigger, Skeleton, Spinner, StatusBadge, StatusDot, StatusPill, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, cardVariants, cn, confirm, prompt, statusWord, useTimedOut };
730
995
  //# sourceMappingURL=primitives.mjs.map
731
996
  //# sourceMappingURL=primitives.mjs.map