@bccampus/ui-components 0.2.0 → 0.4.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 (56) hide show
  1. package/dist/banner.js +14 -14
  2. package/dist/button.d.ts +1 -1
  3. package/dist/button.js +17 -17
  4. package/dist/caption.js +7 -8
  5. package/dist/card.js +34 -34
  6. package/dist/composite.d.ts +151 -0
  7. package/dist/composite.js +472 -0
  8. package/dist/generate-tiles-DuagGD1d.js +244 -0
  9. package/dist/horizontal-list.js +32 -32
  10. package/dist/icon-generator.js +43 -270
  11. package/dist/igenerate-tiles.d.ts +43 -0
  12. package/dist/igenerate-tiles.js +7 -0
  13. package/dist/index-U7DVCmS_.js +76 -0
  14. package/dist/input.js +4 -4
  15. package/dist/masked-image-generator.js +18 -18
  16. package/dist/navigation-menu.d.ts +27 -0
  17. package/dist/navigation-menu.js +1139 -0
  18. package/dist/overlay.js +2 -2
  19. package/dist/page-header.d.ts +5 -1
  20. package/dist/page-header.js +10 -9
  21. package/dist/page-section.js +14 -14
  22. package/dist/page.js +4 -4
  23. package/dist/search-input.js +11 -11
  24. package/dist/tag.js +8 -9
  25. package/dist/ui-components.d.ts +30 -2
  26. package/dist/ui-components.js +47 -37
  27. package/package.json +17 -1
  28. package/src/components/ui/banner.tsx +2 -2
  29. package/src/components/ui/button.tsx +8 -7
  30. package/src/components/ui/card.tsx +5 -5
  31. package/src/components/ui/composite/CompositeData.ts +215 -0
  32. package/src/components/ui/composite/CompositeDataItem.ts +144 -0
  33. package/src/components/ui/composite/composite-component-item.tsx +50 -0
  34. package/src/components/ui/composite/composite-component.tsx +100 -0
  35. package/src/components/ui/composite/composite-data-context.tsx +31 -0
  36. package/src/components/ui/composite/index.ts +4 -0
  37. package/src/components/ui/composite/types.ts +81 -0
  38. package/src/components/ui/horizontal-list.tsx +2 -2
  39. package/src/components/ui/index.ts +1 -0
  40. package/src/components/ui/navigation-menu.tsx +165 -0
  41. package/src/components/ui/page-header.tsx +13 -5
  42. package/src/components/ui/page-section.tsx +8 -8
  43. package/src/components/ui/page.tsx +3 -1
  44. package/src/components/ui/popover.tsx +46 -0
  45. package/src/hooks/use-effect-after-mount.ts +27 -0
  46. package/src/hooks/use-id.ts +5 -0
  47. package/src/hooks/use-keyboard-event.ts +144 -0
  48. package/src/lib/object.ts +48 -0
  49. package/src/lib/set-operations.ts +52 -0
  50. package/src/styles/theme.css +7 -7
  51. package/src/styles/typography.css +334 -341
  52. package/tsconfig.node.json +25 -25
  53. package/vite.config.ts +7 -3
  54. package/vite.ladle.config.ts +17 -0
  55. package/dist/index-DlfV3JTY.js +0 -70
  56. package/dist/jsx-runtime-BzflLqGi.js +0 -282
@@ -0,0 +1,165 @@
1
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
2
+ import { ChevronDownIcon } from "lucide-react";
3
+
4
+ import { cn } from "@/lib/utils";
5
+ import { buttonVariants } from "./button";
6
+
7
+ function NavigationMenu({
8
+ className,
9
+ children,
10
+ noViewport = false,
11
+ mega = false,
12
+ ...props
13
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
14
+ noViewport?: boolean;
15
+ mega?: boolean;
16
+ }) {
17
+ return (
18
+ <NavigationMenuPrimitive.Root
19
+ data-slot="navigation-menu"
20
+ data-mega={mega}
21
+ data-viewport={!noViewport || mega}
22
+ className={cn(
23
+ "group/navigation-menu flex max-w-max flex-1 items-center justify-center",
24
+ { relative: !mega },
25
+ className
26
+ )}
27
+ {...props}
28
+ >
29
+ {children}
30
+ {(!noViewport || mega) && <NavigationMenuViewport mega={mega} />}
31
+ </NavigationMenuPrimitive.Root>
32
+ );
33
+ }
34
+
35
+ function NavigationMenuList({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
36
+ return (
37
+ <NavigationMenuPrimitive.List
38
+ data-slot="navigation-menu-list"
39
+ className={cn("group flex flex-1 list-none items-center justify-center gap-1", className)}
40
+ {...props}
41
+ />
42
+ );
43
+ }
44
+
45
+ function NavigationMenuItem({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
46
+ return (
47
+ <NavigationMenuPrimitive.Item data-slot="navigation-menu-item" className={cn("relative", className)} {...props} />
48
+ );
49
+ }
50
+
51
+ const navigationMenuTriggerStyle = buttonVariants({
52
+ variant: "ghost",
53
+ className:
54
+ "data-[state=open]:hover:text-secondary data-[state=open]:focus:text-secondary data-[state=open]:text-secondary",
55
+ });
56
+
57
+ function NavigationMenuTrigger({
58
+ className,
59
+ children,
60
+ ...props
61
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
62
+ return (
63
+ <NavigationMenuPrimitive.Trigger
64
+ data-slot="navigation-menu-trigger"
65
+ className={cn(navigationMenuTriggerStyle, "group", className)}
66
+ {...props}
67
+ >
68
+ {children}{" "}
69
+ <ChevronDownIcon
70
+ className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
71
+ aria-hidden="true"
72
+ />
73
+ </NavigationMenuPrimitive.Trigger>
74
+ );
75
+ }
76
+
77
+ function NavigationMenuContent({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
78
+ return (
79
+ <NavigationMenuPrimitive.Content
80
+ data-slot="navigation-menu-content"
81
+ className={cn(
82
+ "max-md:h-[calc(100vh-var(--spacing-page-nav))] top-0 left-0 min-w-xs w-auto absolute group-data-[mega=true]/navigation-menu:w-full",
83
+ "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
84
+ "group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0",
85
+ "group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
86
+ className
87
+ )}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ function NavigationMenuViewport({
94
+ className,
95
+ mega = false,
96
+ ...props
97
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport> & {
98
+ mega?: boolean;
99
+ }) {
100
+ return (
101
+ <div
102
+ className={cn("absolute top-full left-0 isolate z-50 flex justify-center bg-popover", {
103
+ "min-w-xs": !mega,
104
+ "w-full px-(--spacing-section)": mega,
105
+ })}
106
+ >
107
+ <NavigationMenuPrimitive.Viewport
108
+ data-slot="navigation-menu-viewport"
109
+ className={cn(
110
+ "origin-top-center duration-150 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
111
+ "bg-popover text-popover-foreground relative overflow-hidden w-full h-[var(--radix-navigation-menu-viewport-height)] overscroll-contain",
112
+ {
113
+ "mt-1.5 rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]": !mega,
114
+ "border-b-1 border-complement-1-50": mega,
115
+ },
116
+ className
117
+ )}
118
+ {...props}
119
+ />
120
+ </div>
121
+ );
122
+ }
123
+
124
+ function NavigationMenuLink({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
125
+ return (
126
+ <NavigationMenuPrimitive.Link
127
+ data-slot="navigation-menu-link"
128
+ className={cn(
129
+ "[&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
130
+ className
131
+ )}
132
+ {...props}
133
+ />
134
+ );
135
+ }
136
+
137
+ function NavigationMenuIndicator({
138
+ className,
139
+ ...props
140
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
141
+ return (
142
+ <NavigationMenuPrimitive.Indicator
143
+ data-slot="navigation-menu-indicator"
144
+ className={cn(
145
+ "data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
146
+ className
147
+ )}
148
+ {...props}
149
+ >
150
+ <div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
151
+ </NavigationMenuPrimitive.Indicator>
152
+ );
153
+ }
154
+
155
+ export {
156
+ NavigationMenu,
157
+ NavigationMenuList,
158
+ NavigationMenuItem,
159
+ NavigationMenuContent,
160
+ NavigationMenuTrigger,
161
+ NavigationMenuLink,
162
+ NavigationMenuIndicator,
163
+ NavigationMenuViewport,
164
+ navigationMenuTriggerStyle,
165
+ };
@@ -1,14 +1,22 @@
1
+ import { Slot } from "@radix-ui/react-slot";
1
2
  import { PageSection } from "./page-section";
2
3
  import { LogoBCcampusWithTagline } from "@bccampus/media-kit";
3
4
 
4
- function PageHeader({ children, ...props }: React.ComponentProps<"div">) {
5
+ export type PageHeaderProps = React.ComponentProps<"div"> & {
6
+ asChild?: boolean;
7
+ };
8
+
9
+ function PageHeader({ children, asChild, ...props }: PageHeaderProps) {
10
+ const Comp = asChild ? Slot : "div";
5
11
  return (
6
- <PageSection py="none" className="sticky top-0 right-0 left-0 z-10 bg-background" {...props}>
7
- <div className="h-page-nav flex flex-row flex-nowrap place-items-center border-b-1 border-b-complement-1-50 py-4 gap-12">
8
- <LogoBCcampusWithTagline height="100%" variant="color" />
12
+ <PageSection py="none" className="sticky top-0 right-0 left-0 z-50 bg-background" {...props}>
13
+ <Comp className="h-page-nav overflow-hidden flex flex-row flex-nowrap border-b-1 border-b-complement-1-50 gap-2 sm:gap-12 justify-between items-center sm:justify-start">
14
+ <div className="py-4 h-full">
15
+ <LogoBCcampusWithTagline height="100%" variant="color" />
16
+ </div>
9
17
 
10
18
  {children}
11
- </div>
19
+ </Comp>
12
20
  </PageSection>
13
21
  );
14
22
  }
@@ -5,17 +5,17 @@ const pageSectionVariants = cva("group @container/page-section relative w-full",
5
5
  variants: {
6
6
  px: {
7
7
  none: "px-0",
8
- default: "px-section",
9
- sm: "px-sextion-sm",
10
- lg: "px-section-lg",
11
- xl: "px-section-xl",
8
+ default: "px-(--spacing-section)",
9
+ sm: "px-(--spacing-section-sm)",
10
+ lg: "px-(--spacing-section-lg)",
11
+ xl: "px-(--spacing-section-xl)",
12
12
  },
13
13
  py: {
14
14
  none: "py-0",
15
- default: "py-section",
16
- sm: "py-sextion-sm",
17
- lg: "py-section-lg",
18
- xl: "py-section-xl",
15
+ default: "py-(--spacing-section)",
16
+ sm: "py-(--spacing-section-sm)",
17
+ lg: "py-(--spacing-section-lg)",
18
+ xl: "py-(--spacing-section-xl)",
19
19
  },
20
20
  },
21
21
  defaultVariants: {
@@ -3,7 +3,9 @@ import { cn } from "@/lib/utils";
3
3
  export type PageProps = React.ComponentProps<"div">;
4
4
 
5
5
  function Page({ className, ...props }: PageProps) {
6
- return <div data-slot="page" className={cn("group @container/page relative w-full", className)} {...props} />;
6
+ return (
7
+ <div data-slot="page" className={cn("group/page @container/page relative w-full", className)} {...props} />
8
+ );
7
9
  }
8
10
 
9
11
  export { Page };
@@ -0,0 +1,46 @@
1
+ import * as React from "react"
2
+ import * as PopoverPrimitive from "@radix-ui/react-popover"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ function Popover({
7
+ ...props
8
+ }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
9
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />
10
+ }
11
+
12
+ function PopoverTrigger({
13
+ ...props
14
+ }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
15
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
16
+ }
17
+
18
+ function PopoverContent({
19
+ className,
20
+ align = "center",
21
+ sideOffset = 4,
22
+ ...props
23
+ }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
24
+ return (
25
+ <PopoverPrimitive.Portal>
26
+ <PopoverPrimitive.Content
27
+ data-slot="popover-content"
28
+ align={align}
29
+ sideOffset={sideOffset}
30
+ className={cn(
31
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
32
+ className
33
+ )}
34
+ {...props}
35
+ />
36
+ </PopoverPrimitive.Portal>
37
+ )
38
+ }
39
+
40
+ function PopoverAnchor({
41
+ ...props
42
+ }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
43
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
44
+ }
45
+
46
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
@@ -0,0 +1,27 @@
1
+ import type { DependencyList, EffectCallback } from 'react';
2
+
3
+ import { useEffect, useRef } from 'react';
4
+
5
+ export function useEffectAfterMount(fn: EffectCallback, dependencies?: DependencyList) {
6
+ const mounted = useRef(false);
7
+
8
+ useEffect(
9
+ () => () => {
10
+ mounted.current = false;
11
+ },
12
+ []
13
+ );
14
+
15
+ useEffect(
16
+ () => {
17
+ if (mounted.current) {
18
+ return fn();
19
+ }
20
+
21
+ mounted.current = true;
22
+ return undefined;
23
+ },
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
25
+ dependencies
26
+ );
27
+ }
@@ -0,0 +1,5 @@
1
+ import { useId as reactUseId } from "react";
2
+
3
+ export function useId(id?: string): string {
4
+ return id ?? reactUseId();
5
+ }
@@ -0,0 +1,144 @@
1
+ import type { KeyboardEvent, KeyboardEventHandler } from 'react';
2
+ import { useMemo } from 'react';
3
+
4
+ const MODIFIER_KEYS = new Set(['ctrl', 'shift', 'alt', 'meta']);
5
+
6
+ const KEY_MAPPINGS: Record<string, string> = {
7
+ ' ': 'space',
8
+ };
9
+
10
+ export interface KeyBindings {
11
+ [sequence: string]: (event: KeyboardEvent) => void;
12
+ }
13
+
14
+ interface KeybindingLookupItem {
15
+ sequence: Set<string>;
16
+ handler: (event: KeyboardEvent) => void;
17
+ }
18
+
19
+ interface UseKeyboardEventOptions {
20
+ eventKeyProp: 'key' | 'code';
21
+ }
22
+
23
+ function isSequenceEqual<T>(sequenceA: Set<T>, sequenceB: Set<T>) {
24
+ if (sequenceA.size !== sequenceB.size) return false;
25
+
26
+ for (const element of sequenceB) {
27
+ if (!sequenceA.has(element)) return false;
28
+ }
29
+
30
+ return true;
31
+ }
32
+
33
+ function parseKeybindings(bindings: KeyBindings) {
34
+ const parsedKeybindings: KeybindingLookupItem[] = [];
35
+ for (const [sequence, handler] of Object.entries(bindings)) {
36
+ const parsedSequence = sequence
37
+ .toLowerCase()
38
+ .trim()
39
+ .split(/\s*\+\s*/);
40
+
41
+ if (parsedSequence.length === 1 && MODIFIER_KEYS.has(parsedSequence[0])) {
42
+ console.error(`[useKeyboardEvent] \`${sequence}\`: A key sequence cannot be only a modifier key.`);
43
+ }
44
+ else if (parsedSequence.includes('')) {
45
+ console.error(`[useKeyboardEvent] \`${sequence}\`: Unknown key defined in the sequence.`);
46
+ }
47
+ else {
48
+ parsedKeybindings.push({
49
+ sequence: new Set(parsedSequence),
50
+ handler,
51
+ });
52
+ }
53
+ }
54
+
55
+ return parsedKeybindings;
56
+ }
57
+
58
+ const defaultOptions: UseKeyboardEventOptions = {
59
+ eventKeyProp: 'key',
60
+ };
61
+
62
+ export function keyboardEventHandler(
63
+ bindings: KeyBindings,
64
+ options: UseKeyboardEventOptions = defaultOptions
65
+ ): KeyboardEventHandler {
66
+ const _options = { ...options, ...defaultOptions }
67
+
68
+ const keyBindings = parseKeybindings(bindings);
69
+
70
+ return (event: KeyboardEvent) => {
71
+ const keySequence = new Set<string>();
72
+ const eventKey = event[_options.eventKeyProp];
73
+
74
+ if (event.ctrlKey) keySequence.add('ctrl');
75
+
76
+ if (event.shiftKey) keySequence.add('shift');
77
+
78
+ if (event.altKey) keySequence.add('alt');
79
+
80
+ if (event.metaKey) keySequence.add('meta');
81
+
82
+ if (!KEY_MAPPINGS[eventKey]) keySequence.add(eventKey.toLowerCase());
83
+ else keySequence.add(KEY_MAPPINGS[eventKey]);
84
+
85
+ const matchedSequence = keyBindings.find(keyBinding => isSequenceEqual(keySequence, keyBinding.sequence));
86
+ if (matchedSequence) {
87
+ event.preventDefault();
88
+ event.stopPropagation();
89
+ matchedSequence.handler(event);
90
+ }
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Returns a `KeyboardEventHandler`
96
+ * that checks the defined key binding sequences against a keyboard event
97
+ * and executes the handler of the first matched key binding.
98
+ *
99
+ * Limitations:
100
+ * - Space character (` `) cannot be used in the key sequences.
101
+ * Use the `space` keyword instead.
102
+ * - Plus character (`+`) cannot be used in the key sequences.
103
+ * Use `shit + =` instead.
104
+ *
105
+ * @example
106
+ * ```
107
+ * export function Input({ value, onChange }: Props) {
108
+ * const [inputValue, setInputValue] = useState<string>('');
109
+ *
110
+ * const clearInput = () => {
111
+ * setInputValue('');
112
+ * };
113
+ *
114
+ * const addItem = () => {
115
+ * if (inputValue) {
116
+ * onChange([...value, inputValue]);
117
+ * clearInput();
118
+ * }
119
+ * };
120
+ *
121
+ * const deleteAll = () => {
122
+ * onChange([]);
123
+ * clearInput();
124
+ * };
125
+ *
126
+ * const handleKeyDown = useKeyboardEvent({
127
+ * 'enter': addItem,
128
+ * 'escape': clearInput,
129
+ * 'ctrl+c': clearInput,
130
+ * 'ctrl + shift + c': deleteAll,
131
+ * });
132
+ *
133
+ * return (
134
+ * <input
135
+ * value={inputValue}
136
+ * onChange={event => setInputValue(event.target.value)}
137
+ * onKeyDown={handleKeyDown}
138
+ * />;
139
+ * }
140
+ * ```
141
+ */
142
+ export function useKeyboardEvent(bindings: KeyBindings, options?: UseKeyboardEventOptions) {
143
+ return useMemo(() => keyboardEventHandler(bindings, options), [bindings, options]);
144
+ }
@@ -0,0 +1,48 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ export const get = <T extends object>(object: T, prop: string) => prop
4
+ .split('.')
5
+ .reduce<any>((reducedObject, key) => (reducedObject && key in reducedObject) ? reducedObject[key] : undefined, object);
6
+
7
+ export const set = <T extends object, V>(object: T, prop: string, value: V) => {
8
+ const propChunks = prop.split('.');
9
+ const lastChunk = propChunks.pop();
10
+ if (!lastChunk) return object;
11
+
12
+ const ref = propChunks.reduce<any>((reducedObject, key) => {
13
+ reducedObject[key] = {};
14
+ return reducedObject[key];
15
+ }, object);
16
+
17
+ ref[lastChunk] = value;
18
+
19
+ return object;
20
+ }
21
+
22
+ export const pick = <T extends object>(object: T, props: string[]) => {
23
+
24
+ return props.reduce<Record<string, unknown>>((result, key) => {
25
+
26
+ set(result, key, get(object, key));
27
+
28
+ return result;
29
+ }, {}) as Partial<T>;
30
+ }
31
+
32
+ export const omit = <T extends object>(object: T, props: string[]) => {
33
+ const result: Partial<T> = { ...object };
34
+
35
+ props.forEach(prop => {
36
+ const propChunks = prop.split('.');
37
+ const lastChunk = propChunks.pop();
38
+ if (lastChunk) {
39
+ const ref = propChunks.reduce<any>((reducedObject, key) => (reducedObject && key in reducedObject) ? reducedObject[key] : undefined, result);
40
+ if (ref && lastChunk in ref) delete ref[lastChunk];
41
+ }
42
+ })
43
+
44
+ return result;
45
+ }
46
+
47
+ export const isObject = (object: unknown) => (typeof object === 'object' && !Array.isArray(object) && object !== null);
48
+
@@ -0,0 +1,52 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ export function isSuperset(set: Iterable<any>, subset: Iterable<any>) {
4
+ const _set = set instanceof Set ? set : new Set(set);
5
+
6
+ for (const elem of subset) {
7
+ if (!_set.has(elem)) {
8
+ return false;
9
+ }
10
+ }
11
+ return true;
12
+ }
13
+
14
+ export function union(setA: Iterable<any>, setB: Iterable<any>) {
15
+ const _union = new Set(setA);
16
+ for (const elem of setB) {
17
+ _union.add(elem);
18
+ }
19
+ return _union;
20
+ }
21
+
22
+ export function intersection(setA: Iterable<any>, setB: Iterable<any>) {
23
+ const _setA = setA instanceof Set ? setA : new Set(setA);
24
+
25
+ const _intersection = new Set();
26
+ for (const elem of setB) {
27
+ if (_setA.has(elem)) {
28
+ _intersection.add(elem);
29
+ }
30
+ }
31
+ return _intersection;
32
+ }
33
+
34
+ export function symmetricDifference(setA: Iterable<any>, setB: Iterable<any>) {
35
+ const _difference = new Set(setA);
36
+ for (const elem of setB) {
37
+ if (_difference.has(elem)) {
38
+ _difference.delete(elem);
39
+ } else {
40
+ _difference.add(elem);
41
+ }
42
+ }
43
+ return _difference;
44
+ }
45
+
46
+ export function difference(setA: Iterable<any>, setB: Iterable<any>) {
47
+ const _difference = new Set(setA);
48
+ for (const elem of setB) {
49
+ _difference.delete(elem);
50
+ }
51
+ return _difference;
52
+ }
@@ -69,7 +69,7 @@
69
69
  --secondary: oklch(0.5393 0.0909 200);
70
70
  --secondary-foreground: oklch(0.985 0 0);
71
71
  --muted: oklch(0.967 0.001 286.375);
72
- --muted-foreground: oklch(0.552 0.016 285.938);
72
+ --muted-foreground: oklch(0.45 0.0236 285.938);
73
73
  --accent: oklch(0.967 0.001 286.375);
74
74
  --accent-foreground: oklch(0.3741 0.0774 245.65);
75
75
  --destructive: oklch(0.577 0.245 27.325);
@@ -91,11 +91,11 @@
91
91
  --sidebar-ring: oklch(0.5393 0.0909 199.73);
92
92
 
93
93
  /* Responsive Layout Spacing */
94
- --section-p: --spacing(6);
95
- --card-p: --spacing(6);
94
+ --section-p: --spacing(4);
95
+ --card-p: --spacing(4);
96
96
  --page-gap: --spacing(0);
97
- --card-gap: --spacing(6);
98
- --card-area-gap: --spacing(6);
97
+ --card-gap: --spacing(4);
98
+ --card-area-gap: --spacing(4);
99
99
  --card-item-group-gap: --spacing(2);
100
100
 
101
101
  @media (width >= 40rem) {
@@ -169,8 +169,8 @@
169
169
  }
170
170
 
171
171
  @utility areas-* {
172
- grid-template-areas: --value(*, [*]);
173
- grid-template-columns: --modifier(*, [*]);
172
+ grid-template-areas: --value(*, [ *]);
173
+ grid-template-columns: --modifier(*, [ *]);
174
174
  }
175
175
 
176
176
  @utility stick-to-page {