@galyan/ui 0.0.1 → 0.0.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 (90) hide show
  1. package/package.json +53 -22
  2. package/.turbo/turbo-build.log +0 -20
  3. package/eslint.config.mjs +0 -4
  4. package/src/accordion/Accordion.tsx +0 -88
  5. package/src/accordion/accordion.css +0 -78
  6. package/src/accordion/index.ts +0 -2
  7. package/src/animatednumber/AnimatedNumber.tsx +0 -71
  8. package/src/animatednumber/index.ts +0 -2
  9. package/src/banner/Banner.tsx +0 -84
  10. package/src/banner/banner.css +0 -56
  11. package/src/banner/index.ts +0 -2
  12. package/src/breadcrumb/Breadcrumb.tsx +0 -94
  13. package/src/breadcrumb/breadcrumb.css +0 -58
  14. package/src/breadcrumb/index.ts +0 -2
  15. package/src/button/Button.tsx +0 -98
  16. package/src/button/button.css +0 -190
  17. package/src/button/index.ts +0 -2
  18. package/src/button.tsx +0 -20
  19. package/src/calendar/Calendar.tsx +0 -190
  20. package/src/calendar/calendar.css +0 -142
  21. package/src/calendar/index.ts +0 -2
  22. package/src/card/Card.tsx +0 -88
  23. package/src/card/card.css +0 -115
  24. package/src/card/index.ts +0 -2
  25. package/src/card.tsx +0 -27
  26. package/src/chart/Chart.tsx +0 -186
  27. package/src/chart/index.ts +0 -2
  28. package/src/checkbox/Checkbox.tsx +0 -101
  29. package/src/checkbox/checkbox.css +0 -76
  30. package/src/checkbox/index.ts +0 -2
  31. package/src/chips/Chips.tsx +0 -146
  32. package/src/chips/chips.css +0 -90
  33. package/src/chips/index.ts +0 -2
  34. package/src/code.tsx +0 -11
  35. package/src/datepicker/DatePicker.tsx +0 -104
  36. package/src/datepicker/datepicker.css +0 -22
  37. package/src/datepicker/index.ts +0 -2
  38. package/src/dragdrop/DragDrop.tsx +0 -168
  39. package/src/dragdrop/dragdrop.css +0 -122
  40. package/src/dragdrop/index.ts +0 -2
  41. package/src/dropdown/Dropdown.tsx +0 -176
  42. package/src/dropdown/dropdown.css +0 -118
  43. package/src/dropdown/index.ts +0 -2
  44. package/src/fileupload/FileUpload.tsx +0 -144
  45. package/src/fileupload/fileupload.css +0 -118
  46. package/src/fileupload/index.ts +0 -2
  47. package/src/index.ts +0 -28
  48. package/src/input/Input.tsx +0 -121
  49. package/src/input/index.ts +0 -2
  50. package/src/input/input.css +0 -120
  51. package/src/menu/Menu.tsx +0 -118
  52. package/src/menu/index.ts +0 -2
  53. package/src/menu/menu.css +0 -117
  54. package/src/modal/Modal.tsx +0 -103
  55. package/src/modal/index.ts +0 -2
  56. package/src/modal/modal.css +0 -79
  57. package/src/progressbar/ProgressBar.tsx +0 -68
  58. package/src/progressbar/index.ts +0 -2
  59. package/src/progressbar/progressbar.css +0 -73
  60. package/src/radiogroup/RadioGroup.tsx +0 -73
  61. package/src/radiogroup/index.ts +0 -2
  62. package/src/radiogroup/radiogroup.css +0 -80
  63. package/src/skeleton/Skeleton.tsx +0 -61
  64. package/src/skeleton/index.ts +0 -2
  65. package/src/skeleton/skeleton.css +0 -46
  66. package/src/spinner/Spinner.tsx +0 -29
  67. package/src/spinner/index.ts +0 -2
  68. package/src/spinner/spinner.css +0 -39
  69. package/src/stepper/Stepper.tsx +0 -127
  70. package/src/stepper/index.ts +0 -2
  71. package/src/stepper/stepper.css +0 -141
  72. package/src/tab/Tabs.tsx +0 -71
  73. package/src/tab/index.ts +0 -2
  74. package/src/tab/tab.css +0 -92
  75. package/src/table/Table.tsx +0 -205
  76. package/src/table/index.ts +0 -2
  77. package/src/table/table.css +0 -119
  78. package/src/textarea/Textarea.tsx +0 -102
  79. package/src/textarea/index.ts +0 -2
  80. package/src/textarea/textarea.css +0 -85
  81. package/src/toaster/Toaster.tsx +0 -108
  82. package/src/toaster/index.ts +0 -2
  83. package/src/toaster/toaster.css +0 -113
  84. package/src/toggle/Toggle.tsx +0 -88
  85. package/src/toggle/index.ts +0 -2
  86. package/src/toggle/toggle.css +0 -63
  87. package/src/typography/Typography.tsx +0 -58
  88. package/src/typography/index.ts +0 -2
  89. package/src/typography/typography.css +0 -102
  90. package/tsconfig.json +0 -13
package/package.json CHANGED
@@ -1,43 +1,74 @@
1
1
  {
2
2
  "name": "@galyan/ui",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "React UI Components with Galyan Design System",
5
5
  "author": "Saksham Galyan",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "private": false,
9
+
6
10
  "repository": {
7
11
  "type": "git",
8
- "url": "https://github.com/Sakshamgalyan/Galyan-UI.git"
12
+ "url": "git+https://github.com/Sakshamgalyan/Galyan-UI.git"
9
13
  },
10
- "license": "MIT",
11
- "private": false,
12
- "type": "module",
14
+
15
+ "homepage": "https://github.com/Sakshamgalyan/Galyan-UI",
16
+
17
+ "bugs": {
18
+ "url": "https://github.com/Sakshamgalyan/Galyan-UI/issues"
19
+ },
20
+
21
+ "main": "./dist/index.cjs",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+
13
25
  "exports": {
14
- ".": "./src/index.ts",
15
- "./*": "./src/*/index.ts"
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.cjs"
30
+ }
31
+ },
32
+
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+
39
+ "sideEffects": [
40
+ "*.css"
41
+ ],
42
+
43
+ "publishConfig": {
44
+ "access": "public"
16
45
  },
46
+
17
47
  "scripts": {
48
+ "build": "tsup src/index.ts --format esm,cjs --dts",
18
49
  "lint": "eslint . --max-warnings 0",
19
- "build": "tsup src/index.ts --dts --format esm,cjs",
20
50
  "check-types": "tsc --noEmit"
21
51
  },
22
- "devDependencies": {
23
- "@repo/eslint-config": "workspace:*",
24
- "@repo/typescript-config": "workspace:*",
25
- "@types/node": "^22.15.3",
26
- "@types/react": "19.2.2",
27
- "@types/react-dom": "19.2.2",
28
- "eslint": "^9.39.1",
29
- "tsup": "^8.5.1",
30
- "typescript": "5.9.2"
31
- },
52
+
32
53
  "dependencies": {
33
54
  "@floating-ui/react": "^0.26.28",
34
- "@galyan/theme": "workspace:*",
35
- "react": "^19.2.0",
36
- "react-dom": "^19.2.0",
55
+ "@galyan/theme": "^0.0.1",
37
56
  "recharts": "^2.15.3"
38
57
  },
58
+
39
59
  "peerDependencies": {
40
60
  "react": "^19.0.0",
41
61
  "react-dom": "^19.0.0"
62
+ },
63
+
64
+ "devDependencies": {
65
+ "@repo/eslint-config": "workspace:*",
66
+ "@repo/typescript-config": "workspace:*",
67
+ "@types/node": "^22.15.3",
68
+ "@types/react": "^19.2.2",
69
+ "@types/react-dom": "^19.2.2",
70
+ "eslint": "^9.39.1",
71
+ "tsup": "^8.5.1",
72
+ "typescript": "^5.9.2"
42
73
  }
43
- }
74
+ }
@@ -1,20 +0,0 @@
1
-
2
- > @galyan/ui@0.0.1 build C:\Project\easyLife\galyan\packages\ui
3
- > tsup src/index.ts --dts --format esm,cjs
4
-
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Target: es2022
9
- ESM Build start
10
- CJS Build start
11
- CJS dist\index.cjs 98.06 KB
12
- CJS dist\index.css 59.93 KB
13
- CJS ⚡️ Build success in 401ms
14
- ESM dist\index.css 59.93 KB
15
- ESM dist\index.js 86.43 KB
16
- ESM ⚡️ Build success in 403ms
17
- DTS Build start
18
- DTS ⚡️ Build success in 4423ms
19
- DTS dist\index.d.ts 17.81 KB
20
- DTS dist\index.d.cts 17.81 KB
package/eslint.config.mjs DELETED
@@ -1,4 +0,0 @@
1
- import { config } from "@repo/eslint-config/react-internal";
2
-
3
- /** @type {import("eslint").Linter.Config} */
4
- export default config;
@@ -1,88 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useRef, useState } from 'react';
4
- import './accordion.css';
5
-
6
- export interface AccordionItemDef {
7
- id: string;
8
- title: React.ReactNode;
9
- content: React.ReactNode;
10
- disabled?: boolean;
11
- }
12
-
13
- export interface AccordionProps {
14
- items: AccordionItemDef[];
15
- /** 'single' = only one open at a time, 'multiple' = multiple can be open */
16
- type?: 'single' | 'multiple';
17
- defaultOpen?: string | string[];
18
- flush?: boolean;
19
- className?: string;
20
- }
21
-
22
- function AccordionPanel({ open, children }: { open: boolean; children: React.ReactNode }) {
23
- const ref = useRef<HTMLDivElement>(null);
24
- const height = open ? (ref.current?.scrollHeight ?? 0) : 0;
25
-
26
- return (
27
- <div
28
- className="gy-accordion-panel"
29
- style={{ height, transition: `height 250ms cubic-bezier(0.4, 0, 0.2, 1)`, overflow: 'hidden' }}
30
- aria-hidden={!open}
31
- >
32
- <div ref={ref} className="gy-accordion-panel-inner">
33
- {children}
34
- </div>
35
- </div>
36
- );
37
- }
38
-
39
- export function Accordion({ items, type = 'single', defaultOpen, flush = false, className = '' }: AccordionProps) {
40
- const initOpen = defaultOpen
41
- ? Array.isArray(defaultOpen) ? defaultOpen : [defaultOpen]
42
- : [];
43
-
44
- const [openIds, setOpenIds] = useState<Set<string>>(new Set(initOpen));
45
-
46
- const toggle = (id: string) => {
47
- setOpenIds((prev) => {
48
- const next = new Set(prev);
49
- if (next.has(id)) {
50
- next.delete(id);
51
- } else {
52
- if (type === 'single') next.clear();
53
- next.add(id);
54
- }
55
- return next;
56
- });
57
- };
58
-
59
- return (
60
- <div className={`gy-accordion ${className}`}>
61
- {items.map((item) => {
62
- const isOpen = openIds.has(item.id);
63
- return (
64
- <div
65
- key={item.id}
66
- className={`gy-accordion-item ${flush ? 'gy-accordion-item--flush' : ''}`}
67
- >
68
- <button
69
- type="button"
70
- className={`gy-accordion-trigger ${isOpen ? 'gy-accordion-trigger--open' : ''}`}
71
- onClick={() => !item.disabled && toggle(item.id)}
72
- aria-expanded={isOpen}
73
- disabled={item.disabled}
74
- >
75
- <span className="gy-accordion-title">{item.title}</span>
76
- <span className={`gy-accordion-icon ${isOpen ? 'gy-accordion-icon--open' : ''}`} aria-hidden="true">
77
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
78
- <polyline points="4,6 8,10 12,6" />
79
- </svg>
80
- </span>
81
- </button>
82
- <AccordionPanel open={isOpen}>{item.content}</AccordionPanel>
83
- </div>
84
- );
85
- })}
86
- </div>
87
- );
88
- }
@@ -1,78 +0,0 @@
1
- /* ── Accordion ───────────────────────────────────────────────────────────── */
2
- .gy-accordion {
3
- display: flex;
4
- flex-direction: column;
5
- gap: var(--gy-spacing-1);
6
- width: 100%;
7
- }
8
-
9
- .gy-accordion-item {
10
- border: 1px solid var(--gy-border);
11
- border-radius: var(--gy-radius-lg);
12
- overflow: hidden;
13
- }
14
-
15
- .gy-accordion-item--flush {
16
- border-radius: 0;
17
- border-left: none;
18
- border-right: none;
19
- border-top: none;
20
- }
21
-
22
- .gy-accordion-trigger {
23
- display: flex;
24
- align-items: center;
25
- justify-content: space-between;
26
- width: 100%;
27
- padding: var(--gy-spacing-4) var(--gy-spacing-4);
28
- background: var(--gy-surface);
29
- border: none;
30
- cursor: pointer;
31
- text-align: left;
32
- gap: var(--gy-spacing-3);
33
- transition: background-color var(--gy-duration-fast);
34
- }
35
-
36
- .gy-accordion-trigger:hover {
37
- background: var(--gy-background-subtle);
38
- }
39
-
40
- .gy-accordion-trigger:focus-visible {
41
- outline: 2px solid var(--gy-focus-outline);
42
- outline-offset: -2px;
43
- }
44
-
45
- .gy-accordion-trigger--open {
46
- background: var(--gy-background-subtle);
47
- }
48
-
49
- .gy-accordion-title {
50
- font-size: var(--gy-font-size-sm);
51
- font-weight: var(--gy-font-weight-semibold);
52
- color: var(--gy-text);
53
- flex: 1;
54
- }
55
-
56
- .gy-accordion-icon {
57
- flex-shrink: 0;
58
- color: var(--gy-text-subtle);
59
- transition: transform var(--gy-duration-normal) var(--gy-ease-spring);
60
- }
61
-
62
- .gy-accordion-icon--open {
63
- transform: rotate(180deg);
64
- }
65
-
66
- .gy-accordion-panel {
67
- overflow: hidden;
68
- background: var(--gy-surface);
69
- }
70
-
71
- .gy-accordion-panel-inner {
72
- padding: 0 var(--gy-spacing-4) var(--gy-spacing-4);
73
- font-size: var(--gy-font-size-sm);
74
- color: var(--gy-text-muted);
75
- line-height: var(--gy-line-height-relaxed);
76
- border-top: 1px solid var(--gy-border);
77
- padding-top: var(--gy-spacing-3);
78
- }
@@ -1,2 +0,0 @@
1
- export { Accordion } from './Accordion';
2
- export type { AccordionProps, AccordionItemDef } from './Accordion';
@@ -1,71 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useEffect, useRef, useState } from 'react';
4
-
5
- export interface AnimatedNumberProps {
6
- value: number;
7
- duration?: number;
8
- format?: (n: number) => string;
9
- className?: string;
10
- prefix?: string;
11
- suffix?: string;
12
- decimals?: number;
13
- easing?: 'linear' | 'easeOut' | 'easeInOut';
14
- }
15
-
16
- function easeOut(t: number) { return 1 - Math.pow(1 - t, 3); }
17
- function easeInOut(t: number) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; }
18
-
19
- export function AnimatedNumber({
20
- value,
21
- duration = 1000,
22
- format,
23
- className = '',
24
- prefix = '',
25
- suffix = '',
26
- decimals = 0,
27
- easing = 'easeOut',
28
- }: AnimatedNumberProps) {
29
- const [displayed, setDisplayed] = useState(0);
30
- const startRef = useRef(0);
31
- const startTimeRef = useRef<number | null>(null);
32
- const rafRef = useRef<number>(0);
33
- const prevValueRef = useRef(0);
34
-
35
- useEffect(() => {
36
- startRef.current = prevValueRef.current;
37
- startTimeRef.current = null;
38
-
39
- const easeFn = easing === 'linear' ? (t: number) => t : easing === 'easeOut' ? easeOut : easeInOut;
40
-
41
- const animate = (time: number) => {
42
- if (!startTimeRef.current) startTimeRef.current = time;
43
- const elapsed = time - startTimeRef.current;
44
- const t = Math.min(elapsed / duration, 1);
45
- const eased = easeFn(t);
46
- const current = startRef.current + (value - startRef.current) * eased;
47
- setDisplayed(current);
48
-
49
- if (t < 1) {
50
- rafRef.current = requestAnimationFrame(animate);
51
- } else {
52
- prevValueRef.current = value;
53
- }
54
- };
55
-
56
- cancelAnimationFrame(rafRef.current);
57
- rafRef.current = requestAnimationFrame(animate);
58
-
59
- return () => cancelAnimationFrame(rafRef.current);
60
- }, [value, duration, easing]);
61
-
62
- const formatted = format
63
- ? format(displayed)
64
- : displayed.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
65
-
66
- return (
67
- <span className={`gy-animated-number ${className}`} aria-live="polite" aria-atomic="true">
68
- {prefix}{formatted}{suffix}
69
- </span>
70
- );
71
- }
@@ -1,2 +0,0 @@
1
- export { AnimatedNumber } from './AnimatedNumber';
2
- export type { AnimatedNumberProps } from './AnimatedNumber';
@@ -1,84 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useState } from 'react';
4
- import './banner.css';
5
-
6
- export type BannerVariant = 'info' | 'success' | 'warning' | 'danger' | 'neutral';
7
- export type BannerLayout = 'inline' | 'full';
8
-
9
- const icons: Record<BannerVariant, React.ReactNode> = {
10
- info: (
11
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 3a1 1 0 110 2 1 1 0 010-2zm0 3a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 7z"/></svg>
12
- ),
13
- success: (
14
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm3.78 5.78a.75.75 0 00-1.06-1.06L7 9.44 5.28 7.72a.75.75 0 00-1.06 1.06l2.25 2.25a.75.75 0 001.06 0l4.25-4.25z"/></svg>
15
- ),
16
- warning: (
17
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5a.75.75 0 01.65.375l6 10.5A.75.75 0 0114 13.5H2a.75.75 0 01-.65-1.125l6-10.5A.75.75 0 018 1.5zm0 4a.75.75 0 01.75.75v3a.75.75 0 01-1.5 0v-3A.75.75 0 018 5.5zm0 6.5a1 1 0 110-2 1 1 0 010 2z"/></svg>
18
- ),
19
- danger: (
20
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 3a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 018 4zm0 7.5a1 1 0 110-2 1 1 0 010 2z"/></svg>
21
- ),
22
- neutral: (
23
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 3a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 018 4zm0 7.5a1 1 0 110-2 1 1 0 010 2z"/></svg>
24
- ),
25
- };
26
-
27
- export interface BannerProps {
28
- variant?: BannerVariant;
29
- layout?: BannerLayout;
30
- title?: string;
31
- description?: React.ReactNode;
32
- icon?: React.ReactNode;
33
- dismissible?: boolean;
34
- onDismiss?: () => void;
35
- className?: string;
36
- }
37
-
38
- export function Banner({
39
- variant = 'info',
40
- layout = 'inline',
41
- title,
42
- description,
43
- icon,
44
- dismissible = false,
45
- onDismiss,
46
- className = '',
47
- }: BannerProps) {
48
- const [dismissed, setDismissed] = useState(false);
49
-
50
- if (dismissed) return null;
51
-
52
- const handleDismiss = () => {
53
- setDismissed(true);
54
- onDismiss?.();
55
- };
56
-
57
- return (
58
- <div
59
- className={[
60
- 'gy-banner',
61
- `gy-banner--${variant}`,
62
- `gy-banner--${layout}`,
63
- className,
64
- ]
65
- .filter(Boolean)
66
- .join(' ')}
67
- role="alert"
68
- >
69
- <span className="gy-banner__icon">{icon ?? icons[variant]}</span>
70
- <div className="gy-banner__content">
71
- {title && <div className="gy-banner__title">{title}</div>}
72
- {description && <div className="gy-banner__description">{description}</div>}
73
- </div>
74
- {dismissible && (
75
- <button className="gy-banner__close" onClick={handleDismiss} aria-label="Dismiss">
76
- <svg width="12" height="12" viewBox="0 0 12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
77
- <line x1="1" y1="1" x2="11" y2="11" />
78
- <line x1="11" y1="1" x2="1" y2="11" />
79
- </svg>
80
- </button>
81
- )}
82
- </div>
83
- );
84
- }
@@ -1,56 +0,0 @@
1
- /* ── Banner ──────────────────────────────────────────────────────────────── */
2
- .gy-banner {
3
- display: flex;
4
- align-items: flex-start;
5
- gap: var(--gy-spacing-3);
6
- padding: var(--gy-spacing-3) var(--gy-spacing-4);
7
- border-radius: var(--gy-radius-lg);
8
- border: 1px solid transparent;
9
- position: relative;
10
- }
11
-
12
- .gy-banner--inline { border-radius: var(--gy-radius-lg); }
13
- .gy-banner--full { border-radius: 0; }
14
-
15
- /* Status variants */
16
- .gy-banner--info { background: var(--gy-info-subtle); border-color: var(--gy-info-muted); color: var(--gy-info-emphasis); }
17
- .gy-banner--success { background: var(--gy-success-subtle); border-color: var(--gy-success-muted); color: var(--gy-success-emphasis); }
18
- .gy-banner--warning { background: var(--gy-warning-subtle); border-color: var(--gy-warning-muted); color: var(--gy-warning-emphasis); }
19
- .gy-banner--danger { background: var(--gy-danger-subtle); border-color: var(--gy-danger-muted); color: var(--gy-danger-emphasis); }
20
- .gy-banner--neutral { background: var(--gy-background-muted); border-color: var(--gy-border); color: var(--gy-text-muted); }
21
-
22
- .gy-banner__icon {
23
- flex-shrink: 0;
24
- margin-top: 1px;
25
- }
26
-
27
- .gy-banner__content { flex: 1; }
28
-
29
- .gy-banner__title {
30
- font-size: var(--gy-font-size-sm);
31
- font-weight: var(--gy-font-weight-semibold);
32
- line-height: 1.4;
33
- }
34
-
35
- .gy-banner__description {
36
- font-size: var(--gy-font-size-sm);
37
- line-height: 1.5;
38
- opacity: 0.9;
39
- margin-top: 2px;
40
- }
41
-
42
- .gy-banner__close {
43
- flex-shrink: 0;
44
- display: flex;
45
- align-items: center;
46
- justify-content: center;
47
- width: 1.5rem;
48
- height: 1.5rem;
49
- border-radius: var(--gy-radius-md);
50
- cursor: pointer;
51
- opacity: 0.7;
52
- transition: opacity var(--gy-duration-fast), background var(--gy-duration-fast);
53
- color: inherit;
54
- }
55
-
56
- .gy-banner__close:hover { opacity: 1; background: rgba(0,0,0,0.08); }
@@ -1,2 +0,0 @@
1
- export { Banner } from './Banner';
2
- export type { BannerProps, BannerVariant, BannerLayout } from './Banner';
@@ -1,94 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useState } from 'react';
4
- import './breadcrumb.css';
5
-
6
- export interface BreadcrumbItem {
7
- label: string;
8
- href?: string;
9
- onClick?: () => void;
10
- }
11
-
12
- export interface BreadcrumbProps {
13
- items: BreadcrumbItem[];
14
- separator?: React.ReactNode;
15
- maxItems?: number;
16
- className?: string;
17
- }
18
-
19
- const DefaultSeparator = () => (
20
- <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
21
- <polyline points="4,2 8,6 4,10" />
22
- </svg>
23
- );
24
-
25
- export function Breadcrumb({
26
- items,
27
- separator = <DefaultSeparator />,
28
- maxItems,
29
- className = '',
30
- }: BreadcrumbProps) {
31
- const [expanded, setExpanded] = useState(false);
32
-
33
- const shouldCollapse = maxItems && items.length > maxItems + 1 && !expanded;
34
- const displayed: React.ReactNode[] = [];
35
-
36
- if (shouldCollapse) {
37
- // Always show first and last
38
- const first = items[0]!;
39
- const last = items[items.length - 1]!;
40
-
41
- displayed.push(renderItem(first, false, separator));
42
- displayed.push(
43
- <li key="ellipsis" className="gy-breadcrumb-item">
44
- <span className="gy-breadcrumb-separator">{separator}</span>
45
- <button
46
- className="gy-breadcrumb-ellipsis"
47
- onClick={() => setExpanded(true)}
48
- aria-label="Show more breadcrumbs"
49
- >
50
- •••
51
- </button>
52
- </li>
53
- );
54
- displayed.push(renderItem(last, true, separator));
55
- } else {
56
- items.forEach((item, i) => {
57
- displayed.push(renderItem(item, i === items.length - 1, separator, i));
58
- });
59
- }
60
-
61
- return (
62
- <nav aria-label="Breadcrumb">
63
- <ol className={`gy-breadcrumb ${className}`}>
64
- {displayed}
65
- </ol>
66
- </nav>
67
- );
68
- }
69
-
70
- function renderItem(
71
- item: BreadcrumbItem,
72
- isCurrent: boolean,
73
- separator: React.ReactNode,
74
- key?: number
75
- ) {
76
- return (
77
- <li key={key ?? item.label} className="gy-breadcrumb-item">
78
- {key !== 0 && key !== undefined && (
79
- <span className="gy-breadcrumb-separator" aria-hidden="true">{separator}</span>
80
- )}
81
- {isCurrent ? (
82
- <span className="gy-breadcrumb-current" aria-current="page">{item.label}</span>
83
- ) : (
84
- <a
85
- className="gy-breadcrumb-link"
86
- href={item.href ?? '#'}
87
- onClick={item.onClick ? (e) => { e.preventDefault(); item.onClick?.(); } : undefined}
88
- >
89
- {item.label}
90
- </a>
91
- )}
92
- </li>
93
- );
94
- }
@@ -1,58 +0,0 @@
1
- /* ── Breadcrumb ──────────────────────────────────────────────────────────── */
2
- .gy-breadcrumb {
3
- display: flex;
4
- align-items: center;
5
- flex-wrap: wrap;
6
- gap: 0;
7
- list-style: none;
8
- padding: 0;
9
- margin: 0;
10
- font-size: var(--gy-font-size-sm);
11
- }
12
-
13
- .gy-breadcrumb-item {
14
- display: inline-flex;
15
- align-items: center;
16
- gap: var(--gy-spacing-1-5);
17
- }
18
-
19
- .gy-breadcrumb-link {
20
- color: var(--gy-text-muted);
21
- text-decoration: none;
22
- transition: color var(--gy-duration-fast);
23
- border-radius: var(--gy-radius-sm);
24
- padding: 1px 2px;
25
- }
26
-
27
- .gy-breadcrumb-link:hover { color: var(--gy-primary); }
28
-
29
- .gy-breadcrumb-link:focus-visible {
30
- outline: 2px solid var(--gy-focus-outline);
31
- outline-offset: 2px;
32
- }
33
-
34
- .gy-breadcrumb-current {
35
- color: var(--gy-text);
36
- font-weight: var(--gy-font-weight-medium);
37
- }
38
-
39
- .gy-breadcrumb-separator {
40
- color: var(--gy-text-subtle);
41
- display: flex;
42
- align-items: center;
43
- user-select: none;
44
- margin: 0 var(--gy-spacing-1);
45
- }
46
-
47
- .gy-breadcrumb-ellipsis {
48
- color: var(--gy-text-subtle);
49
- cursor: pointer;
50
- padding: 1px var(--gy-spacing-1);
51
- border-radius: var(--gy-radius-sm);
52
- transition: color var(--gy-duration-fast), background var(--gy-duration-fast);
53
- }
54
-
55
- .gy-breadcrumb-ellipsis:hover {
56
- color: var(--gy-text);
57
- background: var(--gy-background-muted);
58
- }
@@ -1,2 +0,0 @@
1
- export { Breadcrumb } from './Breadcrumb';
2
- export type { BreadcrumbProps, BreadcrumbItem } from './Breadcrumb';