@optigrit/optigrit-ui 0.0.1

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.
package/bin/cli.js ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ /** Published package name (scoped). Keep in sync with package.json "name". */
10
+ const PKG = '@optigrit/optigrit-ui';
11
+ const PKG_NODE_MODULES = `./node_modules/${PKG}`;
12
+
13
+ async function init() {
14
+ const args = process.argv.slice(2);
15
+ if (args[0] !== 'init') {
16
+ console.log(`Usage: npx optigrit-ui init or npx ${PKG} init`);
17
+ return;
18
+ }
19
+
20
+ const configNames = ['tailwind.config.js', 'tailwind.config.cjs', 'tailwind.config.mjs', 'tailwind.config.ts'];
21
+ const configPath = configNames.map(name => path.resolve(process.cwd(), name)).find(fs.existsSync);
22
+
23
+ if (!configPath) {
24
+ console.error('❌ Could not find tailwind.config file in the root directory.');
25
+ process.exit(1);
26
+ }
27
+
28
+ try {
29
+ let content = fs.readFileSync(configPath, 'utf8');
30
+
31
+ const legacyPresetPaths =
32
+ content.includes('./node_modules/optigrit-ui/tailwind.preset') ||
33
+ content.includes('optigrit-ui/tailwind.preset');
34
+ const scopedPresetPaths =
35
+ content.includes(`${PKG_NODE_MODULES}/tailwind.preset`) ||
36
+ content.includes(`${PKG}/tailwind.preset`);
37
+
38
+ // Update or add import statement
39
+ if (legacyPresetPaths || scopedPresetPaths) {
40
+ content = content.replace(/\.\/node_modules\/optigrit-ui\/tailwind\.preset/g, `${PKG}/preset`);
41
+ content = content.replace(/optigrit-ui\/tailwind\.preset/g, `${PKG}/preset`);
42
+ content = content.replace(
43
+ new RegExp(`\\.\\/node_modules\\/${PKG.replace(/\//g, '\\/')}\\/tailwind\\.preset`, 'g'),
44
+ `${PKG}/preset`,
45
+ );
46
+ content = content.replace(new RegExp(`${PKG.replace(/\//g, '\\/')}\\/tailwind\\.preset`, 'g'), `${PKG}/preset`);
47
+ } else if (!content.includes(`${PKG}/preset`)) {
48
+ content = `import preset from '${PKG}/preset'\n` + content;
49
+ }
50
+
51
+ const legacyContentGlob = './node_modules/optigrit-ui/**/*.{js,jsx,ts,tsx}';
52
+ const scopedContentGlob = `${PKG_NODE_MODULES}/**/*.{js,jsx,ts,tsx}`;
53
+
54
+ // Modify content array
55
+ if (!content.includes(legacyContentGlob) && !content.includes(scopedContentGlob)) {
56
+ content = content.replace(/content:\s*\[([\s\S]*?)\]/, (match, p1) => {
57
+ return `content: [\n "${scopedContentGlob}",\n ${p1.trim().replace(/\n\s*/g, '\n ')}\n ]`;
58
+ });
59
+ }
60
+
61
+ // Modify presets array
62
+ if (!content.includes('presets:')) {
63
+ // If module.exports exists, inject presets array inside
64
+ if (content.includes('module.exports = {')) {
65
+ content = content.replace(/module\.exports\s*=\s*\{/, "module.exports = {\n presets: [preset],");
66
+ } else if (content.includes('export default {')) {
67
+ content = content.replace(/export\s+default\s*\{/, "export default {\n presets: [preset],");
68
+ }
69
+ } else if (!content.includes('preset')) {
70
+ // If presets array already exists but preset is missing
71
+ content = content.replace(/presets:\s*\[([\s\S]*?)\]/, (match, p1) => {
72
+ return `presets: [preset${p1.trim() ? ', ' + p1.trim() : ''}]`;
73
+ });
74
+ }
75
+
76
+ fs.writeFileSync(configPath, content, 'utf8');
77
+
78
+ // Copy index.css to src/ folder
79
+ const srcPath = path.resolve(process.cwd(), 'src');
80
+ if (!fs.existsSync(srcPath)) {
81
+ fs.mkdirSync(srcPath, { recursive: true });
82
+ }
83
+ const sourceCssPath = path.resolve(__dirname, '../index.css');
84
+ const targetCssPath = path.resolve(srcPath, 'index.css');
85
+
86
+ if (fs.existsSync(sourceCssPath)) {
87
+ fs.copyFileSync(sourceCssPath, targetCssPath);
88
+ console.log('✅ Copied index.css to src/index.css');
89
+ } else {
90
+ console.warn(`⚠️ Could not find index.css in ${PKG} package.`);
91
+ }
92
+
93
+ console.log(`✅ ${PKG} initialized successfully!`);
94
+ } catch (error) {
95
+ console.error('❌ Error modifying tailwind.config file or copying index.css:', error);
96
+ }
97
+ }
98
+
99
+ init();
@@ -0,0 +1,40 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, RefObject, ReactNode, InputHTMLAttributes } from 'react';
3
+
4
+ type PopoverPosition = `${'top' | 'bottom' | 'left' | 'right'}-${'start' | 'center' | 'end'}`;
5
+ type PopoverProps = HTMLAttributes<HTMLDivElement> & {
6
+ targetRef: RefObject<HTMLElement | null>;
7
+ position?: PopoverPosition;
8
+ open?: boolean;
9
+ onClose?: () => void;
10
+ onOpen?: (node: HTMLDivElement) => void;
11
+ };
12
+
13
+ type InputProps = {
14
+ label?: string;
15
+ labelShrink?: boolean;
16
+ required?: boolean;
17
+ startIcon?: ReactNode;
18
+ endIcon?: ReactNode;
19
+ bgColor?: string;
20
+ textColor?: string;
21
+ borderColor?: string;
22
+ primaryColor?: string;
23
+ fullWidth?: boolean;
24
+ inputContainerProps?: HTMLAttributes<HTMLDivElement>;
25
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, 'required'>;
26
+ declare const Input: react.ForwardRefExoticComponent<{
27
+ label?: string;
28
+ labelShrink?: boolean;
29
+ required?: boolean;
30
+ startIcon?: ReactNode;
31
+ endIcon?: ReactNode;
32
+ bgColor?: string;
33
+ textColor?: string;
34
+ borderColor?: string;
35
+ primaryColor?: string;
36
+ fullWidth?: boolean;
37
+ inputContainerProps?: HTMLAttributes<HTMLDivElement>;
38
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, "required"> & react.RefAttributes<HTMLInputElement>>;
39
+
40
+ export { Input as I, type PopoverProps as P, type InputProps as a };
@@ -0,0 +1,17 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import react__default from 'react';
3
+
4
+ type Theme = "dark" | "light" | "system";
5
+ type ThemeProviderProps = {
6
+ children: react__default.ReactNode;
7
+ defaultTheme?: Theme;
8
+ storageKey?: string;
9
+ };
10
+ type ThemeProviderState = {
11
+ theme: Theme;
12
+ setTheme: (theme: Theme) => void;
13
+ };
14
+ declare function ThemeProvider({ children, defaultTheme, storageKey, ...props }: ThemeProviderProps): react_jsx_runtime.JSX.Element;
15
+ declare const useTheme: () => ThemeProviderState;
16
+
17
+ export { type Theme as T, ThemeProvider as a, type ThemeProviderProps as b, type ThemeProviderState as c, useTheme as u };
@@ -0,0 +1,465 @@
1
+ import {
2
+ colorMix
3
+ } from "./chunk-MCQS3QNN.js";
4
+
5
+ // src/core/Popover/Popover.tsx
6
+ import { Fragment, useEffect, useLayoutEffect, useRef } from "react";
7
+
8
+ // src/core/Popover/Popover.utils.ts
9
+ function calculatePopoverStyle(container, defaultPosition) {
10
+ const rect = container.getBoundingClientRect();
11
+ const { top, left, bottom, right, width, height } = rect;
12
+ const [position, align = "center"] = defaultPosition.split("-");
13
+ const style = {};
14
+ let transform = "";
15
+ const round = (v) => Math.round(v);
16
+ if (["top", "bottom"].includes(position ?? "")) {
17
+ if (position === "top") style.bottom = `${round(window.innerHeight - top)}px`;
18
+ if (position === "bottom") style.top = `${round(bottom)}px`;
19
+ if (align === "start") style.left = `${round(left)}px`;
20
+ if (align === "end") style.right = `${round(window.innerWidth - right)}px`;
21
+ if (align === "center") {
22
+ style.left = `${round(left + width / 2)}px`;
23
+ transform += " translateX(-50%)";
24
+ style.transformOrigin = "0";
25
+ }
26
+ }
27
+ if (["left", "right"].includes(position ?? "")) {
28
+ if (position === "left") style.right = `${round(window.innerWidth - left)}px`;
29
+ if (position === "right") style.left = `${round(right)}px`;
30
+ if (align === "start") style.top = `${round(top)}px`;
31
+ if (align === "end") style.bottom = `${round(window.innerHeight - bottom)}px`;
32
+ if (align === "center") {
33
+ style.top = `${round(top + height / 2)}px`;
34
+ transform += " translateY(-50%)";
35
+ style.transformOrigin = "50% 0";
36
+ }
37
+ }
38
+ style.transform = transform.trim();
39
+ return style;
40
+ }
41
+
42
+ // src/core/Popover/Popover.tsx
43
+ import { jsx, jsxs } from "react/jsx-runtime";
44
+ function Popover(props) {
45
+ const {
46
+ targetRef,
47
+ children,
48
+ open,
49
+ onClose,
50
+ position: _position = "bottom-center",
51
+ onOpen,
52
+ ...popoverProps
53
+ } = props;
54
+ const popoverRef = useRef(null);
55
+ const popoverPosition = useRef(_position);
56
+ function handlePopoverIntersect() {
57
+ if (!popoverRef.current) return;
58
+ const popoverRect = popoverRef.current.getBoundingClientRect();
59
+ if (popoverRect.top < 0) {
60
+ popoverPosition.current = popoverPosition.current.replace("top", "bottom");
61
+ } else if (popoverRect.bottom > window.innerHeight) {
62
+ popoverPosition.current = popoverPosition.current.replace("bottom", "top");
63
+ }
64
+ if (popoverRect.left < 0) {
65
+ popoverPosition.current = popoverPosition.current.replace("left", "right");
66
+ } else if (popoverRect.right > window.innerWidth) {
67
+ popoverPosition.current = popoverPosition.current.replace("right", "left");
68
+ }
69
+ handleResize();
70
+ }
71
+ function showPopover() {
72
+ if (!popoverRef.current) return;
73
+ popoverPosition.current = _position;
74
+ popoverRef.current.style.display = "flex";
75
+ setTimeout(() => {
76
+ if (!popoverRef.current) return;
77
+ popoverRef.current.style.opacity = "1";
78
+ popoverRef.current.style.scale = "1";
79
+ handlePopoverIntersect();
80
+ onOpen?.(popoverRef.current);
81
+ }, 10);
82
+ }
83
+ function hidePopover() {
84
+ if (!popoverRef.current) return;
85
+ popoverRef.current.style.opacity = "0";
86
+ popoverRef.current.style.scale = "0.8";
87
+ setTimeout(() => {
88
+ if (!popoverRef.current) return;
89
+ popoverRef.current.style.display = "none";
90
+ }, 200);
91
+ }
92
+ function handleClickOutside(event) {
93
+ if (event.target !== event.currentTarget) return;
94
+ onClose?.();
95
+ }
96
+ function handleResize() {
97
+ if (!popoverRef.current || !targetRef.current) return;
98
+ Object.assign(
99
+ popoverRef.current.style,
100
+ calculatePopoverStyle(targetRef.current, popoverPosition.current)
101
+ );
102
+ }
103
+ useLayoutEffect(() => {
104
+ if (!popoverRef.current || !targetRef.current) return;
105
+ handleResize();
106
+ const resizeObserver = new ResizeObserver(handlePopoverIntersect);
107
+ resizeObserver.observe(popoverRef.current);
108
+ resizeObserver.observe(window.document.body);
109
+ window.addEventListener("scroll", handlePopoverIntersect, true);
110
+ return () => {
111
+ resizeObserver.disconnect();
112
+ window.removeEventListener("scroll", handlePopoverIntersect, true);
113
+ };
114
+ }, [targetRef?.current]);
115
+ useLayoutEffect(() => {
116
+ if (_position) popoverPosition.current = _position;
117
+ handleResize();
118
+ }, [_position]);
119
+ useEffect(() => {
120
+ if (open) showPopover();
121
+ else hidePopover();
122
+ }, [open]);
123
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
124
+ /* @__PURE__ */ jsx(
125
+ "div",
126
+ {
127
+ className: "fixed w-[100dvw] h-[100dvh] left-0 top-0",
128
+ style: { display: open ? "block" : "none" },
129
+ onClick: handleClickOutside
130
+ }
131
+ ),
132
+ /* @__PURE__ */ jsx(
133
+ "div",
134
+ {
135
+ ...popoverProps,
136
+ ref: popoverRef,
137
+ style: {
138
+ display: "flex",
139
+ position: "fixed",
140
+ transition: "all 200ms",
141
+ opacity: 0,
142
+ scale: 0.8,
143
+ ...popoverProps.style
144
+ },
145
+ children
146
+ }
147
+ )
148
+ ] });
149
+ }
150
+
151
+ // src/shared/utils/helper.ts
152
+ function cn(...classes) {
153
+ return classes.filter(Boolean).join(" ");
154
+ }
155
+
156
+ // src/core/Input/Input.tsx
157
+ import { forwardRef, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState } from "react";
158
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
159
+ var Input = forwardRef((props, ref) => {
160
+ const {
161
+ label,
162
+ startIcon,
163
+ endIcon,
164
+ required,
165
+ fullWidth = false,
166
+ labelShrink = false,
167
+ bgColor = "white",
168
+ textColor = "black",
169
+ borderColor = "black",
170
+ primaryColor = "royalblue",
171
+ inputContainerProps = {},
172
+ ...inputProps
173
+ } = props;
174
+ const container = useRef2(null);
175
+ const [isFocus, setIsFocus] = useState(false);
176
+ function handleOnFocus(e) {
177
+ handleResize();
178
+ setIsFocus(true);
179
+ props.onFocus?.(e);
180
+ }
181
+ function handleOnBlur(e) {
182
+ handleResize();
183
+ setIsFocus(false);
184
+ props.onBlur?.(e);
185
+ }
186
+ function handleResize() {
187
+ if (!container.current) return;
188
+ const { offsetWidth: startIconWidth = 0 } = container.current.querySelector("#start-icon");
189
+ const { offsetWidth: endIconWidth = 0 } = container.current.querySelector("#end-icon");
190
+ const label2 = container.current.querySelector("#label");
191
+ const input = container.current.querySelector("input");
192
+ Object.assign(label2.style, document.activeElement === input || input.value || labelShrink === void 0 || labelShrink ? {
193
+ left: "2px",
194
+ backgroundColor: bgColor,
195
+ transform: "translateY(-22px) scale(0.75)"
196
+ } : {
197
+ left: `${startIconWidth + 8}px`,
198
+ backgroundColor: "transparent",
199
+ transform: "translateY(0) scale(1)"
200
+ });
201
+ Object.assign(input.style, {
202
+ paddingLeft: startIcon ? `${startIconWidth + 8}px` : "8px",
203
+ paddingRight: endIcon ? `${endIconWidth + 8}px` : "8px"
204
+ });
205
+ }
206
+ useLayoutEffect2(() => {
207
+ if (!container.current) return;
208
+ const resizeObserver = new ResizeObserver(handleResize);
209
+ resizeObserver.observe(container.current);
210
+ handleResize();
211
+ return () => {
212
+ resizeObserver.disconnect();
213
+ };
214
+ }, [container.current]);
215
+ return /* @__PURE__ */ jsxs2(
216
+ "div",
217
+ {
218
+ ...inputContainerProps,
219
+ ref: container,
220
+ style: {
221
+ "--bg-color": bgColor,
222
+ "--text-color": textColor,
223
+ "--placeholder-color": colorMix(textColor, 80),
224
+ "--border-color": colorMix(borderColor, 50),
225
+ "--border-color-hover": colorMix(borderColor, 100),
226
+ "--light-primary-color": colorMix(primaryColor, 50),
227
+ "--primary-color": colorMix(primaryColor, 100),
228
+ width: fullWidth === void 0 || fullWidth ? "100%" : "auto",
229
+ ...inputContainerProps?.style
230
+ },
231
+ className: cn(
232
+ "flex items-center gap-1 border rounded-md h-[44px] p-1 transition-all duration-200 relative bg-[var(--bg-color)]",
233
+ isFocus ? "ring-2 ring-[var(--light-primary-color)] border-[var(--primary-color)]" : "border-[var(--border-color)] hover:border-[var(--border-color-hover)]",
234
+ inputContainerProps?.className
235
+ ),
236
+ children: [
237
+ /* @__PURE__ */ jsx2(
238
+ "div",
239
+ {
240
+ id: "label",
241
+ className: "absolute bg-[var(--bg-color)] text-[var(--text-color)] px-1 rounded-sm transition-all duration-200 select-none cursor-text",
242
+ onClick: () => container.current?.querySelector("input")?.focus(),
243
+ children: label
244
+ }
245
+ ),
246
+ /* @__PURE__ */ jsx2("div", { id: "start-icon", className: "absolute left-0 h-full flex items-center justify-center", children: startIcon }),
247
+ /* @__PURE__ */ jsx2(
248
+ "input",
249
+ {
250
+ ...inputProps,
251
+ value: inputProps.value,
252
+ ref,
253
+ required: required === void 0 || required,
254
+ className: cn(
255
+ "w-full h-full border-none outline-none rounded-sm transition-all duration-200 placeholder-[var(--placeholder-color)] text-[var(--text-color)] [&:-webkit-autofill]:[-webkit-text-fill-color:var(--text-color)] bg-[var(--bg-color)]",
256
+ isFocus || !label || labelShrink === void 0 || labelShrink ? "" : "placeholder:opacity-0",
257
+ inputProps.className
258
+ ),
259
+ onFocus: handleOnFocus,
260
+ onBlur: handleOnBlur
261
+ }
262
+ ),
263
+ /* @__PURE__ */ jsx2("div", { id: "end-icon", className: "absolute right-0 h-full flex items-center justify-center", children: endIcon })
264
+ ]
265
+ }
266
+ );
267
+ });
268
+
269
+ // src/core/ShowWithAnimation/index.tsx
270
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef3, useState as useState2 } from "react";
271
+
272
+ // src/core/ShowWithAnimation/const.ts
273
+ var defaultAnimationStyle = {
274
+ fade: {
275
+ children: {
276
+ from: {
277
+ opacity: "0",
278
+ scale: "0.95"
279
+ },
280
+ active: {
281
+ opacity: "1",
282
+ scale: "1"
283
+ },
284
+ to: {
285
+ opacity: "0",
286
+ scale: "1.05"
287
+ }
288
+ }
289
+ },
290
+ slide: {
291
+ children: {
292
+ from: {
293
+ opacity: "0",
294
+ scale: "0.95",
295
+ transform: "translateY(20px)"
296
+ },
297
+ active: {
298
+ opacity: "1",
299
+ scale: "1",
300
+ transform: "translateY(0px)"
301
+ }
302
+ }
303
+ }
304
+ };
305
+
306
+ // src/core/ShowWithAnimation/index.tsx
307
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
308
+ function ShowWithAnimation(props) {
309
+ const {
310
+ when,
311
+ children,
312
+ containerProps,
313
+ animationStyle,
314
+ otherwise = null,
315
+ removeOnHide = false,
316
+ animationType = "fade",
317
+ animationDuration = 300,
318
+ ...wrapperProps
319
+ } = props;
320
+ if (animationStyle) {
321
+ if (!animationStyle.children.to) {
322
+ animationStyle.children.to = animationStyle.children.from;
323
+ }
324
+ if (!animationStyle.otherwise) {
325
+ animationStyle.otherwise = animationStyle.children;
326
+ }
327
+ if (!animationStyle.otherwise.to) {
328
+ animationStyle.otherwise.to = animationStyle.otherwise.from;
329
+ }
330
+ }
331
+ const defaultAnimationStyle2 = (() => {
332
+ const animationStyle2 = defaultAnimationStyle[animationType];
333
+ if (!animationStyle2.children.to) {
334
+ animationStyle2.children.to = animationStyle2.children.from;
335
+ }
336
+ if (!animationStyle2.otherwise) {
337
+ animationStyle2.otherwise = animationStyle2.children;
338
+ }
339
+ if (!animationStyle2.otherwise.to) {
340
+ animationStyle2.otherwise.to = animationStyle2.otherwise.from;
341
+ }
342
+ return animationStyle2;
343
+ })();
344
+ const childrenContainer = useRef3(null);
345
+ const otherwiseContainer = useRef3(null);
346
+ const [isChildrenVisible, setIsChildrenVisible] = useState2(when);
347
+ const [isOtherwiseVisible, setIsOtherwiseVisible] = useState2(!when);
348
+ function handleShow() {
349
+ setIsChildrenVisible(true);
350
+ setTimeout(() => {
351
+ if (!childrenContainer.current) return;
352
+ Object.assign(childrenContainer.current.style, {
353
+ visibility: "visible",
354
+ ...defaultAnimationStyle2?.children.active,
355
+ ...animationStyle?.children.active,
356
+ transitionDuration: `${animationDuration}ms`
357
+ });
358
+ }, 10);
359
+ if (!otherwiseContainer.current) return;
360
+ Object.assign(otherwiseContainer.current.style, {
361
+ visibility: "hidden",
362
+ ...defaultAnimationStyle2?.otherwise?.to,
363
+ ...animationStyle?.otherwise?.to,
364
+ transitionDuration: `${animationDuration}ms`
365
+ });
366
+ otherwiseContainer.current.addEventListener("transitionend", handleOtherwiseTransitionEnd, { once: true });
367
+ }
368
+ function handleHide() {
369
+ setIsOtherwiseVisible(true);
370
+ setTimeout(() => {
371
+ if (!otherwiseContainer.current) return;
372
+ Object.assign(otherwiseContainer.current.style, {
373
+ visibility: "visible",
374
+ ...defaultAnimationStyle2?.otherwise?.active,
375
+ ...animationStyle?.otherwise?.active,
376
+ transitionDuration: `${animationDuration}ms`
377
+ });
378
+ }, 10);
379
+ if (!childrenContainer.current) return;
380
+ Object.assign(childrenContainer.current.style, {
381
+ visibility: "hidden",
382
+ ...defaultAnimationStyle2?.children.to,
383
+ ...animationStyle?.children.to,
384
+ transitionDuration: `${animationDuration}ms`
385
+ });
386
+ childrenContainer.current.addEventListener("transitionend", handleChildrenTransitionEnd, { once: true });
387
+ }
388
+ function handleChildrenTransitionEnd() {
389
+ if (!childrenContainer.current) return;
390
+ if (childrenContainer.current?.style.visibility === "visible") return;
391
+ if (removeOnHide) return setIsChildrenVisible(false);
392
+ Object.assign(childrenContainer.current.style, {
393
+ ...defaultAnimationStyle2?.children.from,
394
+ ...animationStyle?.children.from,
395
+ transitionDuration: "0ms"
396
+ });
397
+ }
398
+ function handleOtherwiseTransitionEnd() {
399
+ if (!otherwiseContainer.current) return;
400
+ if (otherwiseContainer.current?.style.visibility === "visible") return;
401
+ if (removeOnHide) return setIsOtherwiseVisible(false);
402
+ Object.assign(otherwiseContainer.current.style, {
403
+ ...defaultAnimationStyle2?.otherwise?.from,
404
+ ...animationStyle?.otherwise?.from,
405
+ transitionDuration: "0ms"
406
+ });
407
+ }
408
+ useLayoutEffect3(() => {
409
+ when ? handleShow() : handleHide();
410
+ }, [when]);
411
+ return removeOnHide && !isChildrenVisible && otherwise === null ? null : /* @__PURE__ */ jsxs3(
412
+ "div",
413
+ {
414
+ ...containerProps,
415
+ className: cn("relative flex items-center justify-center", containerProps?.className),
416
+ style: { ...containerProps?.style, position: "relative" },
417
+ children: [
418
+ isChildrenVisible ? /* @__PURE__ */ jsx3(
419
+ "div",
420
+ {
421
+ ...wrapperProps,
422
+ ref: childrenContainer,
423
+ id: "children-container",
424
+ style: {
425
+ ...defaultAnimationStyle2?.children.from,
426
+ ...animationStyle?.children.from,
427
+ ...wrapperProps.style,
428
+ position: when ? "relative" : otherwise ? "absolute" : "relative"
429
+ },
430
+ children
431
+ }
432
+ ) : null,
433
+ otherwise && isOtherwiseVisible ? /* @__PURE__ */ jsx3(
434
+ "div",
435
+ {
436
+ ...wrapperProps,
437
+ ref: otherwiseContainer,
438
+ id: "otherwise-container",
439
+ style: {
440
+ ...defaultAnimationStyle2?.otherwise?.from,
441
+ ...animationStyle?.otherwise?.from,
442
+ transitionDuration: `all ${animationDuration}ms`,
443
+ ...wrapperProps.style,
444
+ position: when ? "absolute" : "relative"
445
+ },
446
+ children: otherwise
447
+ }
448
+ ) : null
449
+ ]
450
+ }
451
+ );
452
+ }
453
+
454
+ // src/core/Show/index.tsx
455
+ function Show({ when, children, otherwise = null }) {
456
+ return when ? children : otherwise;
457
+ }
458
+
459
+ export {
460
+ Popover,
461
+ cn,
462
+ Input,
463
+ ShowWithAnimation,
464
+ Show
465
+ };
@@ -0,0 +1,76 @@
1
+ // src/hooks/useKeyboardShortcuts.ts
2
+ import { useEffect } from "react";
3
+ function useKeyboardShortcuts(shortcuts) {
4
+ function handleKeyDown(event) {
5
+ for (const shortcut of shortcuts) {
6
+ const { key, callback, options = {} } = shortcut;
7
+ if ([
8
+ event.key.toLowerCase() === key.toLowerCase(),
9
+ !!options.ctrl === event.ctrlKey,
10
+ !!options.shift === event.shiftKey,
11
+ !!options.alt === event.altKey,
12
+ !!options.meta === event.metaKey,
13
+ options.customKeyCheck ? options.customKeyCheck(event) : true
14
+ ].some((con) => con === false)) continue;
15
+ if (options.preventDefault) {
16
+ event.preventDefault();
17
+ }
18
+ callback(event);
19
+ }
20
+ }
21
+ useEffect(() => {
22
+ window.addEventListener("keydown", handleKeyDown);
23
+ return () => {
24
+ window.removeEventListener("keydown", handleKeyDown);
25
+ };
26
+ }, [shortcuts]);
27
+ }
28
+
29
+ // src/hooks/useTabNavigation.ts
30
+ import { useLayoutEffect, useSyncExternalStore } from "react";
31
+ function useTabNavigation(options = {}) {
32
+ const {
33
+ defaultTab: _defaultTab,
34
+ tabs = [],
35
+ urlKey = "tab",
36
+ replaceTab = false
37
+ } = options;
38
+ const defaultTab = _defaultTab ?? tabs[0];
39
+ const tab = useSyncExternalStore(subscribeToTabChange, getCurrentTab);
40
+ function subscribeToTabChange(callback) {
41
+ window.addEventListener("popstate", callback);
42
+ return () => {
43
+ window.removeEventListener("popstate", callback);
44
+ };
45
+ }
46
+ function getCurrentTab() {
47
+ const params = new URLSearchParams(window.location.search);
48
+ if (!params.has(urlKey)) return defaultTab;
49
+ return params.get(urlKey);
50
+ }
51
+ function setTab(newTab, replace = replaceTab) {
52
+ if (tab.length && !tabs.includes(newTab)) {
53
+ console.warn(`Invalid tab: ${newTab}. Valid tabs are: ${tabs.join(", ")}`);
54
+ return;
55
+ }
56
+ const params = new URLSearchParams(window.location.search);
57
+ params.set(urlKey, newTab);
58
+ const newUrl = `${window.location.pathname}?${params.toString()}`;
59
+ if (replace) {
60
+ window.history.replaceState({}, "", newUrl);
61
+ window.dispatchEvent(new PopStateEvent("popstate"));
62
+ } else {
63
+ window.history.pushState({}, "", newUrl);
64
+ window.dispatchEvent(new PopStateEvent("popstate"));
65
+ }
66
+ }
67
+ useLayoutEffect(() => {
68
+ setTab(tab, true);
69
+ }, []);
70
+ return { tab, setTab };
71
+ }
72
+
73
+ export {
74
+ useKeyboardShortcuts,
75
+ useTabNavigation
76
+ };