@intlayer/design-system 7.4.0 → 7.5.0-canary.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/dist/esm/components/Avatar/index.mjs +11 -28
- package/dist/esm/components/Avatar/index.mjs.map +1 -1
- package/dist/esm/components/Badge/index.mjs +1 -1
- package/dist/esm/components/Badge/index.mjs.map +1 -1
- package/dist/esm/components/Breadcrumb/index.mjs +1 -0
- package/dist/esm/components/Breadcrumb/index.mjs.map +1 -1
- package/dist/esm/components/Button/Button.mjs +20 -15
- package/dist/esm/components/Button/Button.mjs.map +1 -1
- package/dist/esm/components/Container/index.mjs +7 -4
- package/dist/esm/components/Container/index.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +2 -4
- package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
- package/dist/esm/components/DropDown/index.mjs +6 -2
- package/dist/esm/components/DropDown/index.mjs.map +1 -1
- package/dist/esm/components/Form/elements/CheckboxElement.mjs +8 -4
- package/dist/esm/components/Form/elements/CheckboxElement.mjs.map +1 -1
- package/dist/esm/components/Form/elements/OTPElement.mjs +1 -5
- package/dist/esm/components/Form/elements/OTPElement.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockClient.mjs +1 -1
- package/dist/esm/components/IDE/CodeBlockClient.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockShiki.mjs +1 -0
- package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
- package/dist/esm/components/Input/Input.mjs.map +1 -1
- package/dist/esm/components/Input/OTPInput.mjs +1 -2
- package/dist/esm/components/Input/OTPInput.mjs.map +1 -1
- package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs +225 -0
- package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs.map +1 -0
- package/dist/esm/components/KeyboardShortcut/index.mjs +3 -0
- package/dist/esm/components/Link/Link.mjs +123 -93
- package/dist/esm/components/Link/Link.mjs.map +1 -1
- package/dist/esm/components/Link/index.mjs +2 -2
- package/dist/esm/components/Navbar/DesktopNavbar.mjs +1 -1
- package/dist/esm/components/Navbar/DesktopNavbar.mjs.map +1 -1
- package/dist/esm/components/Pagination/Pagination.mjs +83 -43
- package/dist/esm/components/Pagination/Pagination.mjs.map +1 -1
- package/dist/esm/components/Popover/static.mjs +3 -6
- package/dist/esm/components/Popover/static.mjs.map +1 -1
- package/dist/esm/components/SwitchSelector/index.mjs +2 -2
- package/dist/esm/components/SwitchSelector/index.mjs.map +1 -1
- package/dist/esm/components/Tag/index.mjs +2 -2
- package/dist/esm/components/Tag/index.mjs.map +1 -1
- package/dist/esm/components/Terminal/Terminal.mjs +4 -1
- package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
- package/dist/esm/components/Terminal/terminal.content.mjs +51 -0
- package/dist/esm/components/Terminal/terminal.content.mjs.map +1 -0
- package/dist/esm/components/index.mjs +3 -2
- package/dist/esm/hooks/index.mjs +2 -2
- package/dist/esm/hooks/reactQuery.mjs +8 -1
- package/dist/esm/hooks/reactQuery.mjs.map +1 -1
- package/dist/esm/hooks/useItemSelector.mjs +51 -28
- package/dist/esm/hooks/useItemSelector.mjs.map +1 -1
- package/dist/esm/libs/auth.mjs +9 -3
- package/dist/esm/libs/auth.mjs.map +1 -1
- package/dist/types/components/Avatar/index.d.ts.map +1 -1
- package/dist/types/components/Badge/index.d.ts.map +1 -1
- package/dist/types/components/Breadcrumb/breadcrumb.content.d.ts +3 -3
- package/dist/types/components/Breadcrumb/breadcrumb.content.d.ts.map +1 -1
- package/dist/types/components/Breadcrumb/index.d.ts.map +1 -1
- package/dist/types/components/Button/Button.d.ts +1 -1
- package/dist/types/components/Button/Button.d.ts.map +1 -1
- package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +2 -2
- package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts.map +1 -1
- package/dist/types/components/Command/index.d.ts +17 -17
- package/dist/types/components/Command/index.d.ts.map +1 -1
- package/dist/types/components/Container/index.d.ts +10 -8
- package/dist/types/components/Container/index.d.ts.map +1 -1
- package/dist/types/components/CopyButton/CopyButton.content.d.ts +3 -3
- package/dist/types/components/CopyButton/CopyButton.content.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.d.ts +25 -25
- package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.content.d.ts +9 -9
- package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.d.ts +33 -33
- package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.content.d.ts +25 -25
- package/dist/types/components/DictionaryFieldEditor/DictionaryFieldEditor.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/NavigationView/navigationViewNode.content.d.ts +25 -25
- package/dist/types/components/DictionaryFieldEditor/SaveForm/saveForm.content.d.ts +33 -33
- package/dist/types/components/DictionaryFieldEditor/StructureView/structureView.content.d.ts +9 -9
- package/dist/types/components/DictionaryFieldEditor/VersionSwitcherDropDown/versionSwitcherDropDown.content.d.ts +7 -7
- package/dist/types/components/DictionaryFieldEditor/VersionSwitcherDropDown/versionSwitcherDropDown.content.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/dictionaryFieldEditor.content.d.ts +5 -5
- package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts +31 -31
- package/dist/types/components/DictionaryFieldEditor/nodeTypeSelector.content.d.ts.map +1 -1
- package/dist/types/components/ExpandCollapse/expandCollapse.content.d.ts +3 -3
- package/dist/types/components/Form/FormBase.d.ts +2 -2
- package/dist/types/components/Form/FormField.d.ts +2 -2
- package/dist/types/components/Form/FormItem.d.ts +2 -2
- package/dist/types/components/Form/FormItem.d.ts.map +1 -1
- package/dist/types/components/Form/elements/CheckboxElement.d.ts.map +1 -1
- package/dist/types/components/Form/elements/EditableFieldInputElement.d.ts +2 -2
- package/dist/types/components/Form/elements/EditableFieldTextAreaElement.d.ts +2 -2
- package/dist/types/components/Form/elements/FormElement.d.ts +2 -2
- package/dist/types/components/Form/elements/MultiselectElement.d.ts +2 -2
- package/dist/types/components/Form/elements/OTPElement.d.ts.map +1 -1
- package/dist/types/components/Form/elements/SelectElement.d.ts +2 -2
- package/dist/types/components/Form/elements/SwitchSelectorElement.d.ts +2 -2
- package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeContext.d.ts +2 -2
- package/dist/types/components/IDE/FileTree.d.ts.map +1 -1
- package/dist/types/components/IDE/code.content.d.ts +5 -5
- package/dist/types/components/IDE/code.content.d.ts.map +1 -1
- package/dist/types/components/IDE/copyCode.content.d.ts +5 -5
- package/dist/types/components/IDE/copyCode.content.d.ts.map +1 -1
- package/dist/types/components/Input/Checkbox.d.ts +3 -3
- package/dist/types/components/Input/Checkbox.d.ts.map +1 -1
- package/dist/types/components/Input/Input.d.ts +3 -3
- package/dist/types/components/Input/Input.d.ts.map +1 -1
- package/dist/types/components/Input/OTPInput.d.ts +7 -9
- package/dist/types/components/Input/OTPInput.d.ts.map +1 -1
- package/dist/types/components/Input/SearchInput.d.ts +2 -2
- package/dist/types/components/KeyboardShortcut/KeyboardShortcut.d.ts +90 -0
- package/dist/types/components/KeyboardShortcut/KeyboardShortcut.d.ts.map +1 -0
- package/dist/types/components/KeyboardShortcut/index.d.ts +2 -0
- package/dist/types/components/Link/Link.d.ts +25 -88
- package/dist/types/components/Link/Link.d.ts.map +1 -1
- package/dist/types/components/Link/index.d.ts +2 -2
- package/dist/types/components/Loader/index.content.d.ts +3 -3
- package/dist/types/components/Loader/index.content.d.ts.map +1 -1
- package/dist/types/components/Loader/spinner.d.ts +2 -2
- package/dist/types/components/Loader/spinner.d.ts.map +1 -1
- package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts +17 -17
- package/dist/types/components/LocaleSwitcherContentDropDown/localeSwitcher.content.d.ts.map +1 -1
- package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts +13 -13
- package/dist/types/components/LocaleSwitcherDropDown/localeSwitcher.content.d.ts.map +1 -1
- package/dist/types/components/MaxWidthSmoother/index.d.ts +2 -2
- package/dist/types/components/MaxWidthSmoother/index.d.ts.map +1 -1
- package/dist/types/components/Navbar/Burger.d.ts +2 -2
- package/dist/types/components/Navbar/Burger.d.ts.map +1 -1
- package/dist/types/components/Navbar/DesktopNavbar.d.ts +2 -2
- package/dist/types/components/Navbar/MobileNavbar.d.ts +2 -2
- package/dist/types/components/Navbar/MobileNavbar.d.ts.map +1 -1
- package/dist/types/components/Navbar/index.d.ts +2 -2
- package/dist/types/components/Navbar/index.d.ts.map +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts +3 -3
- package/dist/types/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/types/components/Pagination/pagination.content.d.ts +11 -11
- package/dist/types/components/Popover/static.d.ts +1 -17
- package/dist/types/components/Popover/static.d.ts.map +1 -1
- package/dist/types/components/Select/Select.d.ts +3 -3
- package/dist/types/components/SocialNetworks/index.d.ts +2 -2
- package/dist/types/components/SocialNetworks/index.d.ts.map +1 -1
- package/dist/types/components/SwitchSelector/index.d.ts +5 -5
- package/dist/types/components/SwitchSelector/index.d.ts.map +1 -1
- package/dist/types/components/Tab/Tab.d.ts +6 -6
- package/dist/types/components/Tab/Tab.d.ts.map +1 -1
- package/dist/types/components/Tab/TabContext.d.ts +2 -2
- package/dist/types/components/TabSelector/TabSelector.d.ts +4 -4
- package/dist/types/components/Table/table.content.d.ts +3 -3
- package/dist/types/components/Tag/index.d.ts +2 -2
- package/dist/types/components/Tag/index.d.ts.map +1 -1
- package/dist/types/components/Terminal/Terminal.d.ts.map +1 -1
- package/dist/types/components/Terminal/terminal.content.d.ts +93 -0
- package/dist/types/components/Terminal/terminal.content.d.ts.map +1 -0
- package/dist/types/components/Toaster/Toast.d.ts +2 -2
- package/dist/types/components/Toaster/Toast.d.ts.map +1 -1
- package/dist/types/components/Toaster/Toaster.d.ts +2 -2
- package/dist/types/components/Toaster/Toaster.d.ts.map +1 -1
- package/dist/types/components/index.d.ts +4 -2
- package/dist/types/hooks/index.d.ts +2 -2
- package/dist/types/hooks/reactQuery.d.ts +2 -1
- package/dist/types/hooks/reactQuery.d.ts.map +1 -1
- package/dist/types/libs/auth.d.ts +1 -0
- package/dist/types/libs/auth.d.ts.map +1 -1
- package/package.json +40 -34
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../utils/cn.mjs";
|
|
4
|
+
import { useDevice } from "../../hooks/useDevice.mjs";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/components/KeyboardShortcut/KeyboardShortcut.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Enum for available keyboard keys
|
|
11
|
+
*/
|
|
12
|
+
let KeyList = /* @__PURE__ */ function(KeyList$1) {
|
|
13
|
+
KeyList$1["⌘"] = "⌘";
|
|
14
|
+
KeyList$1["Ctrl"] = "Ctrl";
|
|
15
|
+
KeyList$1["Alt"] = "Alt";
|
|
16
|
+
KeyList$1["⌥"] = "⌥";
|
|
17
|
+
KeyList$1["Shift"] = "Shift";
|
|
18
|
+
KeyList$1["Meta"] = "Meta";
|
|
19
|
+
KeyList$1["F"] = "F";
|
|
20
|
+
KeyList$1["K"] = "K";
|
|
21
|
+
KeyList$1["L"] = "L";
|
|
22
|
+
KeyList$1["P"] = "P";
|
|
23
|
+
KeyList$1["S"] = "S";
|
|
24
|
+
KeyList$1["A"] = "A";
|
|
25
|
+
KeyList$1["B"] = "B";
|
|
26
|
+
KeyList$1["C"] = "C";
|
|
27
|
+
KeyList$1["D"] = "D";
|
|
28
|
+
KeyList$1["E"] = "E";
|
|
29
|
+
KeyList$1["G"] = "G";
|
|
30
|
+
KeyList$1["H"] = "H";
|
|
31
|
+
KeyList$1["I"] = "I";
|
|
32
|
+
KeyList$1["J"] = "J";
|
|
33
|
+
KeyList$1["M"] = "M";
|
|
34
|
+
KeyList$1["N"] = "N";
|
|
35
|
+
KeyList$1["O"] = "O";
|
|
36
|
+
KeyList$1["Q"] = "Q";
|
|
37
|
+
KeyList$1["R"] = "R";
|
|
38
|
+
KeyList$1["T"] = "T";
|
|
39
|
+
KeyList$1["U"] = "U";
|
|
40
|
+
KeyList$1["V"] = "V";
|
|
41
|
+
KeyList$1["W"] = "W";
|
|
42
|
+
KeyList$1["X"] = "X";
|
|
43
|
+
KeyList$1["Y"] = "Y";
|
|
44
|
+
KeyList$1["Z"] = "Z";
|
|
45
|
+
KeyList$1["Enter"] = "Enter";
|
|
46
|
+
KeyList$1["Escape"] = "Escape";
|
|
47
|
+
KeyList$1["Backspace"] = "Backspace";
|
|
48
|
+
KeyList$1["Tab"] = "Tab";
|
|
49
|
+
KeyList$1["Space"] = "Space";
|
|
50
|
+
KeyList$1["ArrowUp"] = "ArrowUp";
|
|
51
|
+
KeyList$1["ArrowDown"] = "ArrowDown";
|
|
52
|
+
KeyList$1["ArrowLeft"] = "ArrowLeft";
|
|
53
|
+
KeyList$1["ArrowRight"] = "ArrowRight";
|
|
54
|
+
KeyList$1["↑"] = "↑";
|
|
55
|
+
KeyList$1["↓"] = "↓";
|
|
56
|
+
KeyList$1["←"] = "←";
|
|
57
|
+
KeyList$1["→"] = "→";
|
|
58
|
+
return KeyList$1;
|
|
59
|
+
}({});
|
|
60
|
+
/**
|
|
61
|
+
* Parse keyboard shortcut string into individual keys
|
|
62
|
+
*/
|
|
63
|
+
const parseShortcut = (shortcut) => {
|
|
64
|
+
return shortcut.split(" + ").map((key) => key.trim());
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Normalize key name for event comparison
|
|
68
|
+
*/
|
|
69
|
+
const normalizeKey = (key) => {
|
|
70
|
+
return {
|
|
71
|
+
"⌘": "Meta",
|
|
72
|
+
Ctrl: "Control",
|
|
73
|
+
Control: "Control",
|
|
74
|
+
Alt: "Alt",
|
|
75
|
+
"⌥": "Alt",
|
|
76
|
+
Shift: "Shift",
|
|
77
|
+
Meta: "Meta",
|
|
78
|
+
"↑": "ArrowUp",
|
|
79
|
+
"↓": "ArrowDown",
|
|
80
|
+
"←": "ArrowLeft",
|
|
81
|
+
"→": "ArrowRight",
|
|
82
|
+
ArrowUp: "ArrowUp",
|
|
83
|
+
ArrowDown: "ArrowDown",
|
|
84
|
+
ArrowLeft: "ArrowLeft",
|
|
85
|
+
ArrowRight: "ArrowRight"
|
|
86
|
+
}[key] || key;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Check if the keyboard event matches the shortcut
|
|
90
|
+
*/
|
|
91
|
+
const matchesShortcut = (event, keys) => {
|
|
92
|
+
const normalizedKeys = keys.map(normalizeKey);
|
|
93
|
+
const hasModifiers = {
|
|
94
|
+
Meta: normalizedKeys.includes("Meta"),
|
|
95
|
+
Control: normalizedKeys.includes("Control"),
|
|
96
|
+
Alt: normalizedKeys.includes("Alt"),
|
|
97
|
+
Shift: normalizedKeys.includes("Shift")
|
|
98
|
+
};
|
|
99
|
+
if (hasModifiers.Meta !== event.metaKey || hasModifiers.Control !== event.ctrlKey || hasModifiers.Alt !== event.altKey || hasModifiers.Shift !== event.shiftKey) return false;
|
|
100
|
+
const nonModifierKey = keys.find((key) => ![
|
|
101
|
+
"⌘",
|
|
102
|
+
"Ctrl",
|
|
103
|
+
"Control",
|
|
104
|
+
"Alt",
|
|
105
|
+
"⌥",
|
|
106
|
+
"Shift",
|
|
107
|
+
"Meta"
|
|
108
|
+
].includes(normalizeKey(key)));
|
|
109
|
+
if (!nonModifierKey) return false;
|
|
110
|
+
const normalizedNonModifierKey = normalizeKey(nonModifierKey);
|
|
111
|
+
if (normalizedNonModifierKey.startsWith("Arrow")) return event.key === normalizedNonModifierKey;
|
|
112
|
+
return event.key.toLowerCase() === normalizedNonModifierKey.toLowerCase();
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Get display key symbol for better visual representation
|
|
116
|
+
*/
|
|
117
|
+
const getDisplayKey = (key) => {
|
|
118
|
+
return {
|
|
119
|
+
ArrowUp: "↑",
|
|
120
|
+
ArrowDown: "↓",
|
|
121
|
+
ArrowLeft: "←",
|
|
122
|
+
ArrowRight: "→"
|
|
123
|
+
}[key] || key;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Get display shortcut based on OS (Mac uses ⌘ and ⌥, others use Ctrl and Alt)
|
|
127
|
+
*/
|
|
128
|
+
const getDisplayShortcut = (shortcut, isMac) => {
|
|
129
|
+
let result = shortcut;
|
|
130
|
+
if (isMac) {
|
|
131
|
+
result = result.replace(/Ctrl/g, "⌘");
|
|
132
|
+
result = result.replace(/Alt/g, "⌥");
|
|
133
|
+
} else {
|
|
134
|
+
result = result.replace(/⌘/g, "Ctrl");
|
|
135
|
+
result = result.replace(/⌥/g, "Alt");
|
|
136
|
+
}
|
|
137
|
+
result = result.replace(/ArrowUp/g, "↑");
|
|
138
|
+
result = result.replace(/ArrowDown/g, "↓");
|
|
139
|
+
result = result.replace(/ArrowLeft/g, "←");
|
|
140
|
+
result = result.replace(/ArrowRight/g, "→");
|
|
141
|
+
return result;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* KeyboardShortcut Component
|
|
145
|
+
*
|
|
146
|
+
* A reusable component that displays keyboard shortcuts and listens for key combinations.
|
|
147
|
+
* Automatically adapts to Mac (⌘, ⌥) and Windows/Linux (Ctrl, Alt) conventions.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```tsx
|
|
151
|
+
* <KeyboardShortcut
|
|
152
|
+
* shortcut="⌘ + F"
|
|
153
|
+
* onTriggered={() => setShowSearch(true)}
|
|
154
|
+
* />
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
const KeyboardShortcut = ({ shortcut, onTriggered, display = true, className, size = "md" }) => {
|
|
158
|
+
const { isMac } = useDevice();
|
|
159
|
+
const keys = parseShortcut(getDisplayShortcut(shortcut, isMac ?? false));
|
|
160
|
+
const [pressedKeys, setPressedKeys] = useState(/* @__PURE__ */ new Set());
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
const handleKeyDown = (event) => {
|
|
163
|
+
const target = event.target;
|
|
164
|
+
const isInputField = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
|
|
165
|
+
const currentKey = event.key;
|
|
166
|
+
const normalizedEventKeys = /* @__PURE__ */ new Set();
|
|
167
|
+
if (event.metaKey) normalizedEventKeys.add("⌘");
|
|
168
|
+
if (event.ctrlKey) normalizedEventKeys.add("Ctrl");
|
|
169
|
+
if (event.altKey) normalizedEventKeys.add(isMac ? "⌥" : "Alt");
|
|
170
|
+
if (event.shiftKey) normalizedEventKeys.add("Shift");
|
|
171
|
+
if (currentKey.startsWith("Arrow")) {
|
|
172
|
+
normalizedEventKeys.add(currentKey);
|
|
173
|
+
const arrowSymbol = getDisplayKey(currentKey);
|
|
174
|
+
normalizedEventKeys.add(arrowSymbol);
|
|
175
|
+
} else normalizedEventKeys.add(currentKey.toUpperCase());
|
|
176
|
+
setPressedKeys(normalizedEventKeys);
|
|
177
|
+
if (onTriggered && matchesShortcut(event, keys)) {
|
|
178
|
+
if (isInputField) return;
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
onTriggered();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const handleKeyUp = () => {
|
|
184
|
+
setPressedKeys(/* @__PURE__ */ new Set());
|
|
185
|
+
};
|
|
186
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
187
|
+
window.addEventListener("keyup", handleKeyUp);
|
|
188
|
+
window.addEventListener("blur", handleKeyUp);
|
|
189
|
+
return () => {
|
|
190
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
191
|
+
window.removeEventListener("keyup", handleKeyUp);
|
|
192
|
+
window.removeEventListener("blur", handleKeyUp);
|
|
193
|
+
};
|
|
194
|
+
}, [keys, onTriggered]);
|
|
195
|
+
if (!display) return null;
|
|
196
|
+
/**
|
|
197
|
+
* Check if a key is currently pressed
|
|
198
|
+
*/
|
|
199
|
+
const isKeyPressed = (key) => {
|
|
200
|
+
const upperKey = key.toUpperCase();
|
|
201
|
+
const normalizedKey = normalizeKey(key);
|
|
202
|
+
return pressedKeys.has(key) || pressedKeys.has(upperKey) || pressedKeys.has(normalizedKey) || key === "⌘" && pressedKeys.has("Meta") || key === "Ctrl" && pressedKeys.has("Control") || key === "⌥" && pressedKeys.has("Alt") || key === "Alt" && pressedKeys.has("Alt") || key === "←" && pressedKeys.has("ArrowLeft") || key === "→" && pressedKeys.has("ArrowRight") || key === "↑" && pressedKeys.has("ArrowUp") || key === "↓" && pressedKeys.has("ArrowDown");
|
|
203
|
+
};
|
|
204
|
+
return /* @__PURE__ */ jsx("kbd", {
|
|
205
|
+
className: cn("inline-flex items-center justify-center gap-0.5 p-0.5", "rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl", "font-medium font-sans", "border-1 border-neutral/20 text-neutral", size === "sm" && "text-xs", size === "md" && "text-sm", size === "lg" && "text-base", className),
|
|
206
|
+
children: keys.map((key, index) => {
|
|
207
|
+
const keyId = `${key}-${index}-${shortcut}`;
|
|
208
|
+
const displayKey = getDisplayKey(key);
|
|
209
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
210
|
+
className: "inline-flex items-center",
|
|
211
|
+
children: [index > 0 && /* @__PURE__ */ jsx("span", {
|
|
212
|
+
className: "text-neutral/50",
|
|
213
|
+
children: "+"
|
|
214
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
215
|
+
className: cn("min-w-4 px-0.5", isKeyPressed(key) && "scale-120 font-bold text-text"),
|
|
216
|
+
children: displayKey
|
|
217
|
+
})]
|
|
218
|
+
}, keyId);
|
|
219
|
+
})
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
//#endregion
|
|
224
|
+
export { KeyList, KeyboardShortcut };
|
|
225
|
+
//# sourceMappingURL=KeyboardShortcut.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyboardShortcut.mjs","names":["KeyboardShortcut: FC<KeyboardShortcutProps>"],"sources":["../../../../src/components/KeyboardShortcut/KeyboardShortcut.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useEffect, useState } from 'react';\nimport { useDevice } from '../../hooks/useDevice';\nimport { cn } from '../../utils/cn';\n\n/**\n * Enum for available keyboard keys\n */\nexport enum KeyList {\n '⌘' = '⌘',\n Ctrl = 'Ctrl',\n Alt = 'Alt',\n '⌥' = '⌥',\n Shift = 'Shift',\n Meta = 'Meta',\n F = 'F',\n K = 'K',\n L = 'L',\n P = 'P',\n S = 'S',\n A = 'A',\n B = 'B',\n C = 'C',\n D = 'D',\n E = 'E',\n G = 'G',\n H = 'H',\n I = 'I',\n J = 'J',\n M = 'M',\n N = 'N',\n O = 'O',\n Q = 'Q',\n R = 'R',\n T = 'T',\n U = 'U',\n V = 'V',\n W = 'W',\n X = 'X',\n Y = 'Y',\n Z = 'Z',\n Enter = 'Enter',\n Escape = 'Escape',\n Backspace = 'Backspace',\n Tab = 'Tab',\n Space = 'Space',\n ArrowUp = 'ArrowUp',\n ArrowDown = 'ArrowDown',\n ArrowLeft = 'ArrowLeft',\n ArrowRight = 'ArrowRight',\n '↑' = '↑',\n '↓' = '↓',\n '←' = '←',\n '→' = '→',\n}\n\n/**\n * Type-safe keyboard shortcut combinations\n * Note: Using string type to avoid union type complexity issues\n * Expected format: \"Key + Key\" (e.g., \"⌘ + F\", \"Ctrl + Shift + K\")\n */\nexport type KeyboardShortcutType = string;\n\nexport type KeyboardShortcutProps = {\n /** The keyboard shortcut combination (e.g., \"⌘ + F\" or \"Ctrl + K\") */\n shortcut: KeyboardShortcutType;\n /** Callback function triggered when the shortcut is pressed */\n onTriggered?: () => void;\n /** Whether to display the shortcut visually (default: true) */\n display?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Size of the keyboard shortcut display */\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * Parse keyboard shortcut string into individual keys\n */\nconst parseShortcut = (shortcut: string): string[] => {\n return shortcut.split(' + ').map((key) => key.trim());\n};\n\n/**\n * Normalize key name for event comparison\n */\nconst normalizeKey = (key: string): string => {\n const keyMap: Record<string, string> = {\n '⌘': 'Meta',\n Ctrl: 'Control',\n Control: 'Control',\n Alt: 'Alt',\n '⌥': 'Alt',\n Shift: 'Shift',\n Meta: 'Meta',\n '↑': 'ArrowUp',\n '↓': 'ArrowDown',\n '←': 'ArrowLeft',\n '→': 'ArrowRight',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n };\n\n return keyMap[key] || key;\n};\n\n/**\n * Check if the keyboard event matches the shortcut\n */\nconst matchesShortcut = (event: KeyboardEvent, keys: string[]): boolean => {\n const normalizedKeys = keys.map(normalizeKey);\n const hasModifiers = {\n Meta: normalizedKeys.includes('Meta'),\n Control: normalizedKeys.includes('Control'),\n Alt: normalizedKeys.includes('Alt'),\n Shift: normalizedKeys.includes('Shift'),\n };\n\n // Check if all required modifiers are pressed\n if (\n hasModifiers.Meta !== event.metaKey ||\n hasModifiers.Control !== event.ctrlKey ||\n hasModifiers.Alt !== event.altKey ||\n hasModifiers.Shift !== event.shiftKey\n ) {\n return false;\n }\n\n // Find the non-modifier key\n const nonModifierKey = keys.find(\n (key) =>\n !['⌘', 'Ctrl', 'Control', 'Alt', '⌥', 'Shift', 'Meta'].includes(\n normalizeKey(key)\n )\n );\n\n if (!nonModifierKey) return false;\n\n // Normalize the key for comparison\n const normalizedNonModifierKey = normalizeKey(nonModifierKey);\n\n // Compare the main key\n // For arrow keys, compare directly with event.key\n if (normalizedNonModifierKey.startsWith('Arrow')) {\n return event.key === normalizedNonModifierKey;\n }\n\n // For other keys, compare case-insensitive\n return event.key.toLowerCase() === normalizedNonModifierKey.toLowerCase();\n};\n\n/**\n * Get display key symbol for better visual representation\n */\nconst getDisplayKey = (key: string): string => {\n const displayMap: Record<string, string> = {\n ArrowUp: '↑',\n ArrowDown: '↓',\n ArrowLeft: '←',\n ArrowRight: '→',\n };\n\n return displayMap[key] || key;\n};\n\n/**\n * Get display shortcut based on OS (Mac uses ⌘ and ⌥, others use Ctrl and Alt)\n */\nconst getDisplayShortcut = (shortcut: string, isMac: boolean): string => {\n let result = shortcut;\n\n if (isMac) {\n result = result.replace(/Ctrl/g, '⌘');\n result = result.replace(/Alt/g, '⌥');\n } else {\n result = result.replace(/⌘/g, 'Ctrl');\n result = result.replace(/⌥/g, 'Alt');\n }\n\n // Replace arrow key names with symbols\n result = result.replace(/ArrowUp/g, '↑');\n result = result.replace(/ArrowDown/g, '↓');\n result = result.replace(/ArrowLeft/g, '←');\n result = result.replace(/ArrowRight/g, '→');\n\n return result;\n};\n\n/**\n * KeyboardShortcut Component\n *\n * A reusable component that displays keyboard shortcuts and listens for key combinations.\n * Automatically adapts to Mac (⌘, ⌥) and Windows/Linux (Ctrl, Alt) conventions.\n *\n * @example\n * ```tsx\n * <KeyboardShortcut\n * shortcut=\"⌘ + F\"\n * onTriggered={() => setShowSearch(true)}\n * />\n * ```\n */\nexport const KeyboardShortcut: FC<KeyboardShortcutProps> = ({\n shortcut,\n onTriggered,\n display = true,\n className,\n size = 'md',\n}) => {\n const { isMac } = useDevice();\n const displayShortcut = getDisplayShortcut(shortcut, isMac ?? false);\n const keys = parseShortcut(displayShortcut);\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // Don't trigger shortcuts when typing in input fields\n const target = event.target as HTMLElement;\n const isInputField =\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable;\n\n // Update pressed keys state for visual feedback\n const currentKey = event.key;\n const normalizedEventKeys = new Set<string>();\n\n // Add modifier keys\n if (event.metaKey) normalizedEventKeys.add('⌘');\n if (event.ctrlKey) normalizedEventKeys.add('Ctrl');\n if (event.altKey) normalizedEventKeys.add(isMac ? '⌥' : 'Alt');\n if (event.shiftKey) normalizedEventKeys.add('Shift');\n\n // Add the main key\n if (currentKey.startsWith('Arrow')) {\n // For arrow keys, add both the key name and the symbol\n normalizedEventKeys.add(currentKey);\n const arrowSymbol = getDisplayKey(currentKey);\n normalizedEventKeys.add(arrowSymbol);\n } else {\n normalizedEventKeys.add(currentKey.toUpperCase());\n }\n\n setPressedKeys(normalizedEventKeys);\n\n // Trigger callback if shortcut matches\n if (onTriggered && matchesShortcut(event, keys)) {\n // Don't trigger shortcuts when typing in input fields\n if (isInputField) {\n return;\n }\n event.preventDefault();\n onTriggered();\n }\n };\n\n const handleKeyUp = () => {\n // Clear pressed keys when any key is released\n setPressedKeys(new Set());\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n window.addEventListener('blur', handleKeyUp); // Clear on window blur\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n window.removeEventListener('blur', handleKeyUp);\n };\n }, [keys, onTriggered]);\n\n if (!display) return null;\n\n /**\n * Check if a key is currently pressed\n */\n const isKeyPressed = (key: string): boolean => {\n const upperKey = key.toUpperCase();\n const normalizedKey = normalizeKey(key);\n\n return (\n pressedKeys.has(key) ||\n pressedKeys.has(upperKey) ||\n pressedKeys.has(normalizedKey) ||\n // Check for modifier key matches\n (key === '⌘' && pressedKeys.has('Meta')) ||\n (key === 'Ctrl' && pressedKeys.has('Control')) ||\n (key === '⌥' && pressedKeys.has('Alt')) ||\n (key === 'Alt' && pressedKeys.has('Alt')) ||\n // Check for arrow key symbols\n (key === '←' && pressedKeys.has('ArrowLeft')) ||\n (key === '→' && pressedKeys.has('ArrowRight')) ||\n (key === '↑' && pressedKeys.has('ArrowUp')) ||\n (key === '↓' && pressedKeys.has('ArrowDown'))\n );\n };\n\n return (\n <kbd\n className={cn(\n 'inline-flex items-center justify-center gap-0.5 p-0.5',\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n 'font-medium font-sans',\n 'border-1 border-neutral/20 text-neutral',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n className\n )}\n >\n {keys.map((key, index) => {\n const keyId = `${key}-${index}-${shortcut}`;\n const displayKey = getDisplayKey(key);\n return (\n <span key={keyId} className=\"inline-flex items-center\">\n {index > 0 && <span className=\"text-neutral/50\">+</span>}\n <span\n className={cn(\n 'min-w-4 px-0.5',\n isKeyPressed(key) && 'scale-120 font-bold text-text'\n )}\n >\n {displayKey}\n </span>\n </span>\n );\n })}\n </kbd>\n );\n};\n"],"mappings":"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"}
|
|
@@ -53,6 +53,26 @@ let LinkColor = /* @__PURE__ */ function(LinkColor$1) {
|
|
|
53
53
|
LinkColor$1["CUSTOM"] = "custom";
|
|
54
54
|
return LinkColor$1;
|
|
55
55
|
}({});
|
|
56
|
+
/** Available rounded corner sizes for the container */
|
|
57
|
+
let LinkRoundedSize = /* @__PURE__ */ function(LinkRoundedSize$1) {
|
|
58
|
+
LinkRoundedSize$1["NONE"] = "none";
|
|
59
|
+
LinkRoundedSize$1["SM"] = "sm";
|
|
60
|
+
LinkRoundedSize$1["MD"] = "md";
|
|
61
|
+
LinkRoundedSize$1["LG"] = "lg";
|
|
62
|
+
LinkRoundedSize$1["XL"] = "xl";
|
|
63
|
+
LinkRoundedSize$1["TWO_XL"] = "2xl";
|
|
64
|
+
LinkRoundedSize$1["THREE_XL"] = "3xl";
|
|
65
|
+
LinkRoundedSize$1["FULL"] = "full";
|
|
66
|
+
return LinkRoundedSize$1;
|
|
67
|
+
}({});
|
|
68
|
+
let LinkSize = /* @__PURE__ */ function(LinkSize$1) {
|
|
69
|
+
LinkSize$1["SM"] = "sm";
|
|
70
|
+
LinkSize$1["MD"] = "md";
|
|
71
|
+
LinkSize$1["LG"] = "lg";
|
|
72
|
+
LinkSize$1["XL"] = "xl";
|
|
73
|
+
LinkSize$1["CUSTOM"] = "custom";
|
|
74
|
+
return LinkSize$1;
|
|
75
|
+
}({});
|
|
56
76
|
/**
|
|
57
77
|
* Underline style options for Link component
|
|
58
78
|
*
|
|
@@ -71,14 +91,24 @@ let LinkUnderlined = /* @__PURE__ */ function(LinkUnderlined$1) {
|
|
|
71
91
|
* Class variance authority configuration for Link component styling
|
|
72
92
|
* Defines the visual appearance based on variant, color, and underline options
|
|
73
93
|
*/
|
|
74
|
-
const linkVariants = cva("gap-3 transition focus-visible:outline-
|
|
94
|
+
const linkVariants = cva("gap-3 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50", {
|
|
75
95
|
variants: {
|
|
76
96
|
variant: {
|
|
77
97
|
[`${LinkVariant.DEFAULT}`]: "h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0 hover:underline",
|
|
78
98
|
[`${LinkVariant.INVISIBLE_LINK}`]: "h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0",
|
|
79
|
-
[`${LinkVariant.BUTTON}`]: "flex
|
|
80
|
-
[`${LinkVariant.BUTTON_OUTLINED}`]: "flex
|
|
81
|
-
[`${LinkVariant.HOVERABLE}`]: "block rounded-lg border-none bg-current/0
|
|
99
|
+
[`${LinkVariant.BUTTON}`]: "relative flex cursor-pointer flex-row items-center justify-center gap-2 rounded-full bg-current text-center font-medium text-text ring-0 *:text-text-opposite hover:bg-current/90 hover:ring-5 aria-selected:ring-5",
|
|
100
|
+
[`${LinkVariant.BUTTON_OUTLINED}`]: "relative flex cursor-pointer flex-row items-center justify-center gap-2 rounded-full border-[1.3px] border-current text-center font-medium text-text ring-0 *:text-text hover:bg-current/20 hover:ring-5 aria-selected:ring-5",
|
|
101
|
+
[`${LinkVariant.HOVERABLE}`]: "block rounded-lg border-none bg-current/0 hover:bg-current/10 aria-[current]:bg-current/5"
|
|
102
|
+
},
|
|
103
|
+
roundedSize: {
|
|
104
|
+
[`${LinkRoundedSize.NONE}`]: "rounded-none",
|
|
105
|
+
[`${LinkRoundedSize.SM}`]: "rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl",
|
|
106
|
+
[`${LinkRoundedSize.MD}`]: "rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl",
|
|
107
|
+
[`${LinkRoundedSize.LG}`]: "rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl",
|
|
108
|
+
[`${LinkRoundedSize.XL}`]: "rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl",
|
|
109
|
+
[`${LinkRoundedSize.TWO_XL}`]: "rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]",
|
|
110
|
+
[`${LinkRoundedSize.THREE_XL}`]: "rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]",
|
|
111
|
+
[`${LinkRoundedSize.FULL}`]: "rounded-full"
|
|
82
112
|
},
|
|
83
113
|
color: {
|
|
84
114
|
[`${LinkColor.PRIMARY}`]: "text-primary",
|
|
@@ -93,16 +123,97 @@ const linkVariants = cva("gap-3 transition focus-visible:outline-hidden disabled
|
|
|
93
123
|
[`${LinkColor.SUCCESS}`]: "text-success",
|
|
94
124
|
[`${LinkColor.CUSTOM}`]: ""
|
|
95
125
|
},
|
|
126
|
+
size: {
|
|
127
|
+
[`${LinkSize.SM}`]: "text-sm",
|
|
128
|
+
[`${LinkSize.MD}`]: "text-base",
|
|
129
|
+
[`${LinkSize.LG}`]: "text-lg",
|
|
130
|
+
[`${LinkSize.XL}`]: "text-xl",
|
|
131
|
+
[`${LinkSize.CUSTOM}`]: ""
|
|
132
|
+
},
|
|
96
133
|
underlined: {
|
|
97
134
|
[LinkUnderlined.DEFAULT]: "",
|
|
98
135
|
[LinkUnderlined.TRUE]: "underline",
|
|
99
136
|
[LinkUnderlined.FALSE]: "no-underline"
|
|
100
137
|
}
|
|
101
138
|
},
|
|
139
|
+
compoundVariants: [
|
|
140
|
+
{
|
|
141
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
142
|
+
size: LinkSize.SM,
|
|
143
|
+
class: "min-h-7 px-3 max-md:py-1"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
147
|
+
size: LinkSize.MD,
|
|
148
|
+
class: "min-h-8 px-6 max-md:py-2"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
152
|
+
size: LinkSize.LG,
|
|
153
|
+
class: "min-h-10 px-8 max-md:py-3"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
157
|
+
size: LinkSize.XL,
|
|
158
|
+
class: "min-h-11 px-10 max-md:py-4"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
162
|
+
color: LinkColor.PRIMARY,
|
|
163
|
+
class: "ring-primary/20"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
167
|
+
color: LinkColor.SECONDARY,
|
|
168
|
+
class: "ring-secondary/20"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
172
|
+
color: LinkColor.DESTRUCTIVE,
|
|
173
|
+
class: "ring-destructive/20"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
177
|
+
color: LinkColor.NEUTRAL,
|
|
178
|
+
class: "ring-neutral/20"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
182
|
+
color: LinkColor.LIGHT,
|
|
183
|
+
class: "ring-white/20"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
187
|
+
color: LinkColor.DARK,
|
|
188
|
+
class: "ring-neutral-800/20"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
192
|
+
color: LinkColor.TEXT,
|
|
193
|
+
class: "ring-text/20"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
197
|
+
color: LinkColor.TEXT_INVERSE,
|
|
198
|
+
class: "ring-text-opposite/20"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
202
|
+
color: LinkColor.ERROR,
|
|
203
|
+
class: "ring-error/20"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],
|
|
207
|
+
color: LinkColor.SUCCESS,
|
|
208
|
+
class: "ring-success/20"
|
|
209
|
+
}
|
|
210
|
+
],
|
|
102
211
|
defaultVariants: {
|
|
103
212
|
variant: LinkVariant.DEFAULT,
|
|
104
213
|
color: LinkColor.PRIMARY,
|
|
105
|
-
|
|
214
|
+
roundedSize: LinkRoundedSize.MD,
|
|
215
|
+
underlined: LinkUnderlined.DEFAULT,
|
|
216
|
+
size: LinkSize.MD
|
|
106
217
|
}
|
|
107
218
|
});
|
|
108
219
|
/**
|
|
@@ -113,7 +224,7 @@ const linkVariants = cva("gap-3 transition focus-visible:outline-hidden disabled
|
|
|
113
224
|
*
|
|
114
225
|
* @example
|
|
115
226
|
* ```tsx
|
|
116
|
-
* checkIsExternalLink({ href: 'https://example.com' }) // true
|
|
227
|
+
* checkIsExternalLink({ href: '[https://example.com](https://example.com)' }) // true
|
|
117
228
|
* checkIsExternalLink({ href: '/internal-page' }) // false
|
|
118
229
|
* checkIsExternalLink({ href: '/page', isExternalLink: true }) // true
|
|
119
230
|
* ```
|
|
@@ -127,93 +238,10 @@ const checkIsExternalLink = ({ href, isExternalLink: isExternalLinkProp }) => {
|
|
|
127
238
|
*
|
|
128
239
|
* A versatile link component that handles both internal and external navigation
|
|
129
240
|
* with comprehensive internationalization support and multiple visual variants.
|
|
130
|
-
*
|
|
131
|
-
* ## Key Features
|
|
132
|
-
* - **Multiple Variants**: Default, invisible, button, outlined button, and hoverable styles
|
|
133
|
-
* - **Color Themes**: Comprehensive color palette for different contexts and meanings
|
|
134
|
-
* - **External Link Detection**: Automatic detection and handling of external URLs
|
|
135
|
-
* - **Internationalization**: Built-in support for localized URLs via Intlayer
|
|
136
|
-
* - **Security**: Automatic security attributes for external links (noopener, noreferrer)
|
|
137
|
-
* - **Accessibility**: Full ARIA support with proper labels and current page indication
|
|
138
|
-
* - **Visual Feedback**: Hover effects, underline options, and active states
|
|
139
|
-
*
|
|
140
|
-
* ## Use Cases
|
|
141
|
-
* - Navigation within applications (internal links)
|
|
142
|
-
* - External links to other websites with security measures
|
|
143
|
-
* - Button-styled links for call-to-action scenarios
|
|
144
|
-
* - Subtle hoverable links for navigation menus
|
|
145
|
-
* - Multi-language website navigation with automatic URL localization
|
|
146
|
-
*
|
|
147
|
-
* ## Security Features
|
|
148
|
-
* External links automatically receive security attributes:
|
|
149
|
-
* - `rel="noopener noreferrer nofollow"` - Prevents security vulnerabilities
|
|
150
|
-
* - `target="_blank"` - Opens in new tab/window
|
|
151
|
-
* - External link icon indication for user clarity
|
|
152
|
-
*
|
|
153
|
-
* ## Internationalization
|
|
154
|
-
* When used with Intlayer, the component automatically:
|
|
155
|
-
* - Localizes internal URLs based on the current or specified locale
|
|
156
|
-
* - Sets appropriate `hrefLang` attributes for SEO
|
|
157
|
-
* - Maintains proper URL structure for multi-language sites
|
|
158
|
-
*
|
|
159
|
-
* @component
|
|
160
|
-
* @example
|
|
161
|
-
* ```tsx
|
|
162
|
-
* // Basic internal link
|
|
163
|
-
* <Link href="/about" label="Go to about page">
|
|
164
|
-
* About Us
|
|
165
|
-
* </Link>
|
|
166
|
-
*
|
|
167
|
-
* // External link with auto-detection
|
|
168
|
-
* <Link href="https://example.com" label="Visit external site">
|
|
169
|
-
* External Site
|
|
170
|
-
* </Link>
|
|
171
|
-
*
|
|
172
|
-
* // Button-styled link
|
|
173
|
-
* <Link
|
|
174
|
-
* href="/signup"
|
|
175
|
-
* variant={LinkVariant.BUTTON}
|
|
176
|
-
* color={LinkColor.PRIMARY}
|
|
177
|
-
* label="Sign up for account"
|
|
178
|
-
* >
|
|
179
|
-
* Get Started
|
|
180
|
-
* </Link>
|
|
181
|
-
*
|
|
182
|
-
* // Localized link
|
|
183
|
-
* <Link
|
|
184
|
-
* href="/products"
|
|
185
|
-
* locale="fr"
|
|
186
|
-
* label="Voir les produits"
|
|
187
|
-
* >
|
|
188
|
-
* Produits
|
|
189
|
-
* </Link>
|
|
190
|
-
*
|
|
191
|
-
* // Active navigation link
|
|
192
|
-
* <Link
|
|
193
|
-
* href="/dashboard"
|
|
194
|
-
* isActive={true}
|
|
195
|
-
* variant={LinkVariant.HOVERABLE}
|
|
196
|
-
* label="Current page: Dashboard"
|
|
197
|
-
* >
|
|
198
|
-
* Dashboard
|
|
199
|
-
* </Link>
|
|
200
|
-
* ```
|
|
201
|
-
*
|
|
202
|
-
* @param props - Link component props
|
|
203
|
-
* @param props.children - Content to display inside the link
|
|
204
|
-
* @param props.href - URL or path to navigate to
|
|
205
|
-
* @param props.label - Accessible label describing the link's purpose
|
|
206
|
-
* @param props.variant - Visual style variant
|
|
207
|
-
* @param props.color - Color theme for the link
|
|
208
|
-
* @param props.underlined - Underline visibility option
|
|
209
|
-
* @param props.isExternalLink - Override external link detection
|
|
210
|
-
* @param props.isActive - Whether this link represents the current page
|
|
211
|
-
* @param props.locale - Locale for URL internationalization
|
|
212
|
-
* @param props.className - Additional CSS classes
|
|
213
|
-
* @returns Accessible and internationalized link component
|
|
241
|
+
* ...
|
|
214
242
|
*/
|
|
215
243
|
const Link = (props) => {
|
|
216
|
-
const { variant = LinkVariant.DEFAULT, color = LinkColor.PRIMARY, children, label, className, isActive, underlined, locale, isExternalLink: isExternalLinkProp, isPageSection: isPageSectionProp, href: hrefProp, ...otherProps } = props;
|
|
244
|
+
const { variant = LinkVariant.DEFAULT, color = LinkColor.PRIMARY, roundedSize, children, label, className, isActive, underlined, locale, size, isExternalLink: isExternalLinkProp, isPageSection: isPageSectionProp, href: hrefProp, ...otherProps } = props;
|
|
217
245
|
const isExternalLink = isExternalLinkProp ?? checkIsExternalLink(props);
|
|
218
246
|
const isPageSection = isPageSectionProp ?? hrefProp?.startsWith("#") ?? false;
|
|
219
247
|
const isChildrenString = typeof children === "string";
|
|
@@ -228,12 +256,14 @@ const Link = (props) => {
|
|
|
228
256
|
className: cn(linkVariants({
|
|
229
257
|
variant,
|
|
230
258
|
color,
|
|
259
|
+
roundedSize,
|
|
231
260
|
underlined,
|
|
261
|
+
size,
|
|
232
262
|
className
|
|
233
263
|
})),
|
|
234
264
|
...otherProps,
|
|
235
265
|
children: [
|
|
236
|
-
|
|
266
|
+
children,
|
|
237
267
|
isExternalLink && isChildrenString && /* @__PURE__ */ jsx(ExternalLink, { className: "ml-2 inline-block size-4" }),
|
|
238
268
|
isPageSection && /* @__PURE__ */ jsx(MoveRight, { className: "ml-2 inline-block size-4" })
|
|
239
269
|
]
|
|
@@ -241,5 +271,5 @@ const Link = (props) => {
|
|
|
241
271
|
};
|
|
242
272
|
|
|
243
273
|
//#endregion
|
|
244
|
-
export { Link, LinkColor, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants };
|
|
274
|
+
export { Link, LinkColor, LinkRoundedSize, LinkSize, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants };
|
|
245
275
|
//# sourceMappingURL=Link.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.mjs","names":["Link: FC<LinkProps>"],"sources":["../../../../src/components/Link/Link.tsx"],"sourcesContent":["import { getLocalizedUrl } from '@intlayer/core';\nimport type { LocalesValues } from '@intlayer/types';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { ExternalLink, MoveRight } from 'lucide-react';\nimport type { AnchorHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { cn } from '../../utils/cn';\n\n/**\n * Visual style variants for Link component\n *\n * @enum {string}\n */\nexport enum LinkVariant {\n /** Default underlined link with hover effects */\n DEFAULT = 'default',\n /** Link without visible underline or hover effects */\n INVISIBLE_LINK = 'invisible-link',\n /** Button-styled link with solid background */\n BUTTON = 'button',\n /** Button-styled link with outlined border */\n BUTTON_OUTLINED = 'button-outlined',\n /** Link with subtle hover background effect */\n HOVERABLE = 'hoverable',\n}\n\n/**\n * Color theme variants for Link component\n *\n * @enum {string}\n */\nexport enum LinkColor {\n /** Primary brand color */\n PRIMARY = 'primary',\n /** Secondary brand color */\n SECONDARY = 'secondary',\n /** Destructive/danger color for critical actions */\n DESTRUCTIVE = 'destructive',\n /** Neutral/muted color for less prominent links */\n NEUTRAL = 'neutral',\n /** Light color for dark backgrounds */\n LIGHT = 'light',\n /** Dark color for light backgrounds */\n DARK = 'dark',\n /** Default text color */\n TEXT = 'text',\n /** Inverse text color for opposite backgrounds */\n TEXT_INVERSE = 'text-inverse',\n /** Error/red color for error states */\n ERROR = 'error',\n /** Success/green color for positive actions */\n SUCCESS = 'success',\n /** Custom color - no default styling applied */\n CUSTOM = 'custom',\n}\n\n/**\n * Underline style options for Link component\n *\n * @enum {string}\n */\nexport enum LinkUnderlined {\n /** Default underline behavior based on variant */\n DEFAULT = 'default',\n /** Always show underline */\n TRUE = 'true',\n /** Never show underline */\n FALSE = 'false',\n}\n\n/**\n * Class variance authority configuration for Link component styling\n * Defines the visual appearance based on variant, color, and underline options\n */\nexport const linkVariants = cva(\n 'gap-3 transition focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n variant: {\n [`${LinkVariant.DEFAULT}`]:\n 'h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0 hover:underline',\n [`${LinkVariant.INVISIBLE_LINK}`]:\n 'h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0',\n [`${LinkVariant.BUTTON}`]:\n 'flex min-h-8 items-center justify-center gap-2 whitespace-nowrap rounded-lg bg-current px-6 font-medium text-sm transition *:text-text-opposite focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 max-md:py-2',\n [`${LinkVariant.BUTTON_OUTLINED}`]:\n 'flex min-h-8 items-center justify-center gap-2 whitespace-nowrap rounded-lg border-[1.5px] px-6 font-medium text-sm transition hover:bg-current/30 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 max-md:py-2',\n [`${LinkVariant.HOVERABLE}`]:\n 'block rounded-lg border-none bg-current/0 transition hover:bg-current/20 aria-[current]:bg-current/5',\n },\n color: {\n [`${LinkColor.PRIMARY}`]: 'text-primary',\n [`${LinkColor.SECONDARY}`]: 'text-secondary',\n [`${LinkColor.DESTRUCTIVE}`]: 'text-destructive',\n [`${LinkColor.NEUTRAL}`]: 'text-neutral',\n [`${LinkColor.LIGHT}`]: 'text-white',\n [`${LinkColor.DARK}`]: 'text-neutral-800',\n [`${LinkColor.TEXT}`]: 'text-text',\n [`${LinkColor.TEXT_INVERSE}`]: 'text-text-opposite',\n [`${LinkColor.ERROR}`]: 'text-error',\n [`${LinkColor.SUCCESS}`]: 'text-success',\n [`${LinkColor.CUSTOM}`]: '',\n },\n underlined: {\n [LinkUnderlined.DEFAULT]: '',\n [LinkUnderlined.TRUE]: 'underline',\n [LinkUnderlined.FALSE]: 'no-underline',\n },\n },\n\n defaultVariants: {\n variant: LinkVariant.DEFAULT,\n color: LinkColor.PRIMARY,\n underlined: LinkUnderlined.DEFAULT,\n },\n }\n);\n\n/**\n * Props interface for the Link component\n *\n * @interface LinkProps\n * @extends {DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>}\n * @extends {VariantProps<typeof linkVariants>}\n */\nexport type LinkProps = DetailedHTMLProps<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n HTMLAnchorElement\n> &\n VariantProps<typeof linkVariants> & {\n /**\n * Accessible label for screen readers (required)\n * Provides context about what the link does or where it leads\n * @example \"Navigate to home page\"\n */\n label: string;\n\n /**\n * Whether this link opens in a new tab/window\n * When true, adds target=\"_blank\" and security attributes\n * Auto-detected for URLs starting with http/https when undefined\n * @default undefined (auto-detect based on href)\n */\n isExternalLink?: boolean;\n\n /**\n * If a link is a page section as '#id'\n * @default false\n */\n isPageSection?: boolean;\n\n /**\n * Whether this link represents the current page/active state\n * Adds aria-current=\"page\" for accessibility\n * @default false\n */\n isActive?: boolean;\n\n /**\n * Locale for internationalized URLs\n * When provided, URLs are automatically localized using Intlayer\n * @example 'fr', 'es', 'en'\n */\n locale?: LocalesValues;\n };\n\n/**\n * Utility function to determine if a link should be treated as external\n *\n * @param props - Link component props containing href and isExternalLink\n * @returns {boolean} True if the link should open externally\n *\n * @example\n * ```tsx\n * checkIsExternalLink({ href: 'https://example.com' }) // true\n * checkIsExternalLink({ href: '/internal-page' }) // false\n * checkIsExternalLink({ href: '/page', isExternalLink: true }) // true\n * ```\n */\nexport const checkIsExternalLink = ({\n href,\n isExternalLink: isExternalLinkProp,\n}: LinkProps): boolean => {\n const isValidHref = typeof href === 'string' && href.trim() !== '';\n const isExternalLink =\n isExternalLinkProp === true ||\n (typeof isExternalLinkProp === 'undefined' &&\n isValidHref &&\n /^https?:\\/\\//.test(href));\n\n return isExternalLink;\n};\n\n/**\n * Link Component\n *\n * A versatile link component that handles both internal and external navigation\n * with comprehensive internationalization support and multiple visual variants.\n *\n * ## Key Features\n * - **Multiple Variants**: Default, invisible, button, outlined button, and hoverable styles\n * - **Color Themes**: Comprehensive color palette for different contexts and meanings\n * - **External Link Detection**: Automatic detection and handling of external URLs\n * - **Internationalization**: Built-in support for localized URLs via Intlayer\n * - **Security**: Automatic security attributes for external links (noopener, noreferrer)\n * - **Accessibility**: Full ARIA support with proper labels and current page indication\n * - **Visual Feedback**: Hover effects, underline options, and active states\n *\n * ## Use Cases\n * - Navigation within applications (internal links)\n * - External links to other websites with security measures\n * - Button-styled links for call-to-action scenarios\n * - Subtle hoverable links for navigation menus\n * - Multi-language website navigation with automatic URL localization\n *\n * ## Security Features\n * External links automatically receive security attributes:\n * - `rel=\"noopener noreferrer nofollow\"` - Prevents security vulnerabilities\n * - `target=\"_blank\"` - Opens in new tab/window\n * - External link icon indication for user clarity\n *\n * ## Internationalization\n * When used with Intlayer, the component automatically:\n * - Localizes internal URLs based on the current or specified locale\n * - Sets appropriate `hrefLang` attributes for SEO\n * - Maintains proper URL structure for multi-language sites\n *\n * @component\n * @example\n * ```tsx\n * // Basic internal link\n * <Link href=\"/about\" label=\"Go to about page\">\n * About Us\n * </Link>\n *\n * // External link with auto-detection\n * <Link href=\"https://example.com\" label=\"Visit external site\">\n * External Site\n * </Link>\n *\n * // Button-styled link\n * <Link\n * href=\"/signup\"\n * variant={LinkVariant.BUTTON}\n * color={LinkColor.PRIMARY}\n * label=\"Sign up for account\"\n * >\n * Get Started\n * </Link>\n *\n * // Localized link\n * <Link\n * href=\"/products\"\n * locale=\"fr\"\n * label=\"Voir les produits\"\n * >\n * Produits\n * </Link>\n *\n * // Active navigation link\n * <Link\n * href=\"/dashboard\"\n * isActive={true}\n * variant={LinkVariant.HOVERABLE}\n * label=\"Current page: Dashboard\"\n * >\n * Dashboard\n * </Link>\n * ```\n *\n * @param props - Link component props\n * @param props.children - Content to display inside the link\n * @param props.href - URL or path to navigate to\n * @param props.label - Accessible label describing the link's purpose\n * @param props.variant - Visual style variant\n * @param props.color - Color theme for the link\n * @param props.underlined - Underline visibility option\n * @param props.isExternalLink - Override external link detection\n * @param props.isActive - Whether this link represents the current page\n * @param props.locale - Locale for URL internationalization\n * @param props.className - Additional CSS classes\n * @returns Accessible and internationalized link component\n */\nexport const Link: FC<LinkProps> = (props) => {\n const {\n variant = LinkVariant.DEFAULT,\n color = LinkColor.PRIMARY,\n children,\n label,\n className,\n isActive,\n underlined,\n locale,\n isExternalLink: isExternalLinkProp,\n isPageSection: isPageSectionProp,\n href: hrefProp,\n ...otherProps\n } = props;\n\n const isExternalLink = isExternalLinkProp ?? checkIsExternalLink(props);\n const isPageSection = isPageSectionProp ?? hrefProp?.startsWith('#') ?? false;\n\n const isChildrenString = typeof children === 'string';\n\n const rel = isExternalLink ? 'noopener noreferrer nofollow' : undefined;\n\n const target = isExternalLink ? '_blank' : '_self';\n\n const href =\n locale && hrefProp && !isExternalLink && !isPageSection\n ? getLocalizedUrl(hrefProp, locale)\n : hrefProp;\n\n return (\n <a\n href={href}\n aria-label={label}\n rel={rel}\n target={target}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n linkVariants({\n variant,\n color,\n underlined,\n className,\n })\n )}\n {...otherProps}\n >\n {variant === 'button' ? <span>{children}</span> : children}\n {isExternalLink && isChildrenString && (\n <ExternalLink className=\"ml-2 inline-block size-4\" />\n )}\n {isPageSection && <MoveRight className=\"ml-2 inline-block size-4\" />}\n </a>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAYA,IAAY,sDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;;;AAQF,IAAY,kDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;;;AAQF,IAAY,4DAAL;;AAEL;;AAEA;;AAEA;;;;;;;AAOF,MAAa,eAAe,IAC1B,kGACA;CACE,UAAU;EACR,SAAS;IACN,GAAG,YAAY,YACd;IACD,GAAG,YAAY,mBACd;IACD,GAAG,YAAY,WACd;IACD,GAAG,YAAY,oBACd;IACD,GAAG,YAAY,cACd;GACH;EACD,OAAO;IACJ,GAAG,UAAU,YAAY;IACzB,GAAG,UAAU,cAAc;IAC3B,GAAG,UAAU,gBAAgB;IAC7B,GAAG,UAAU,YAAY;IACzB,GAAG,UAAU,UAAU;IACvB,GAAG,UAAU,SAAS;IACtB,GAAG,UAAU,SAAS;IACtB,GAAG,UAAU,iBAAiB;IAC9B,GAAG,UAAU,UAAU;IACvB,GAAG,UAAU,YAAY;IACzB,GAAG,UAAU,WAAW;GAC1B;EACD,YAAY;IACT,eAAe,UAAU;IACzB,eAAe,OAAO;IACtB,eAAe,QAAQ;GACzB;EACF;CAED,iBAAiB;EACf,SAAS,YAAY;EACrB,OAAO,UAAU;EACjB,YAAY,eAAe;EAC5B;CACF,CACF;;;;;;;;;;;;;;AA+DD,MAAa,uBAAuB,EAClC,MACA,gBAAgB,yBACQ;CACxB,MAAM,cAAc,OAAO,SAAS,YAAY,KAAK,MAAM,KAAK;AAOhE,QALE,uBAAuB,QACtB,OAAO,uBAAuB,eAC7B,eACA,eAAe,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+F/B,MAAaA,QAAuB,UAAU;CAC5C,MAAM,EACJ,UAAU,YAAY,SACtB,QAAQ,UAAU,SAClB,UACA,OACA,WACA,UACA,YACA,QACA,gBAAgB,oBAChB,eAAe,mBACf,MAAM,UACN,GAAG,eACD;CAEJ,MAAM,iBAAiB,sBAAsB,oBAAoB,MAAM;CACvE,MAAM,gBAAgB,qBAAqB,UAAU,WAAW,IAAI,IAAI;CAExE,MAAM,mBAAmB,OAAO,aAAa;CAE7C,MAAM,MAAM,iBAAiB,iCAAiC;CAE9D,MAAM,SAAS,iBAAiB,WAAW;AAO3C,QACE,qBAAC;EACC,MANF,UAAU,YAAY,CAAC,kBAAkB,CAAC,gBACtC,gBAAgB,UAAU,OAAO,GACjC;EAKF,cAAY;EACP;EACG;EACR,gBAAc,WAAW,SAAS;EAClC,WAAW,GACT,aAAa;GACX;GACA;GACA;GACA;GACD,CAAC,CACH;EACD,GAAI;;GAEH,YAAY,WAAW,oBAAC,UAAM,WAAgB,GAAG;GACjD,kBAAkB,oBACjB,oBAAC,gBAAa,WAAU,6BAA6B;GAEtD,iBAAiB,oBAAC,aAAU,WAAU,6BAA6B;;GAClE"}
|
|
1
|
+
{"version":3,"file":"Link.mjs","names":["Link: FC<LinkProps>"],"sources":["../../../../src/components/Link/Link.tsx"],"sourcesContent":["import { getLocalizedUrl } from '@intlayer/core';\nimport type { LocalesValues } from '@intlayer/types';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { ExternalLink, MoveRight } from 'lucide-react';\nimport type { AnchorHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { cn } from '../../utils/cn';\n\n/**\n * Visual style variants for Link component\n *\n * @enum {string}\n */\nexport enum LinkVariant {\n /** Default underlined link with hover effects */\n DEFAULT = 'default',\n /** Link without visible underline or hover effects */\n INVISIBLE_LINK = 'invisible-link',\n /** Button-styled link with solid background */\n BUTTON = 'button',\n /** Button-styled link with outlined border */\n BUTTON_OUTLINED = 'button-outlined',\n /** Link with subtle hover background effect */\n HOVERABLE = 'hoverable',\n}\n\n/**\n * Color theme variants for Link component\n *\n * @enum {string}\n */\nexport enum LinkColor {\n /** Primary brand color */\n PRIMARY = 'primary',\n /** Secondary brand color */\n SECONDARY = 'secondary',\n /** Destructive/danger color for critical actions */\n DESTRUCTIVE = 'destructive',\n /** Neutral/muted color for less prominent links */\n NEUTRAL = 'neutral',\n /** Light color for dark backgrounds */\n LIGHT = 'light',\n /** Dark color for light backgrounds */\n DARK = 'dark',\n /** Default text color */\n TEXT = 'text',\n /** Inverse text color for opposite backgrounds */\n TEXT_INVERSE = 'text-inverse',\n /** Error/red color for error states */\n ERROR = 'error',\n /** Success/green color for positive actions */\n SUCCESS = 'success',\n /** Custom color - no default styling applied */\n CUSTOM = 'custom',\n}\n\n/** Available rounded corner sizes for the container */\nexport enum LinkRoundedSize {\n NONE = 'none',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n TWO_XL = '2xl',\n THREE_XL = '3xl',\n FULL = 'full',\n}\n\nexport enum LinkSize {\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n CUSTOM = 'custom',\n}\n\n/**\n * Underline style options for Link component\n *\n * @enum {string}\n */\nexport enum LinkUnderlined {\n /** Default underline behavior based on variant */\n DEFAULT = 'default',\n /** Always show underline */\n TRUE = 'true',\n /** Never show underline */\n FALSE = 'false',\n}\n\n/**\n * Class variance authority configuration for Link component styling\n * Defines the visual appearance based on variant, color, and underline options\n */\nexport const linkVariants = cva(\n 'gap-3 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n variant: {\n [`${LinkVariant.DEFAULT}`]:\n 'h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0 hover:underline',\n [`${LinkVariant.INVISIBLE_LINK}`]:\n 'h-auto justify-start border-inherit bg-current/0 px-1 underline-offset-4 hover:bg-current/0',\n\n [`${LinkVariant.BUTTON}`]:\n 'relative flex cursor-pointer flex-row items-center justify-center gap-2 rounded-full bg-current text-center font-medium text-text ring-0 *:text-text-opposite hover:bg-current/90 hover:ring-5 aria-selected:ring-5',\n\n [`${LinkVariant.BUTTON_OUTLINED}`]:\n 'relative flex cursor-pointer flex-row items-center justify-center gap-2 rounded-full border-[1.3px] border-current text-center font-medium text-text ring-0 *:text-text hover:bg-current/20 hover:ring-5 aria-selected:ring-5',\n\n [`${LinkVariant.HOVERABLE}`]:\n 'block rounded-lg border-none bg-current/0 hover:bg-current/10 aria-[current]:bg-current/5',\n },\n roundedSize: {\n [`${LinkRoundedSize.NONE}`]: 'rounded-none',\n [`${LinkRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${LinkRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${LinkRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${LinkRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n [`${LinkRoundedSize.TWO_XL}`]:\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n [`${LinkRoundedSize.THREE_XL}`]:\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n [`${LinkRoundedSize.FULL}`]: 'rounded-full',\n },\n color: {\n [`${LinkColor.PRIMARY}`]: 'text-primary',\n [`${LinkColor.SECONDARY}`]: 'text-secondary',\n [`${LinkColor.DESTRUCTIVE}`]: 'text-destructive',\n [`${LinkColor.NEUTRAL}`]: 'text-neutral',\n [`${LinkColor.LIGHT}`]: 'text-white',\n [`${LinkColor.DARK}`]: 'text-neutral-800',\n [`${LinkColor.TEXT}`]: 'text-text',\n [`${LinkColor.TEXT_INVERSE}`]: 'text-text-opposite',\n [`${LinkColor.ERROR}`]: 'text-error',\n [`${LinkColor.SUCCESS}`]: 'text-success',\n [`${LinkColor.CUSTOM}`]: '',\n },\n size: {\n [`${LinkSize.SM}`]: 'text-sm',\n [`${LinkSize.MD}`]: 'text-base',\n [`${LinkSize.LG}`]: 'text-lg',\n [`${LinkSize.XL}`]: 'text-xl',\n [`${LinkSize.CUSTOM}`]: '',\n },\n underlined: {\n [LinkUnderlined.DEFAULT]: '',\n [LinkUnderlined.TRUE]: 'underline',\n [LinkUnderlined.FALSE]: 'no-underline',\n },\n },\n // Compound variants handle height and padding\n compoundVariants: [\n // Min height and padding for button variants\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n size: LinkSize.SM,\n class: 'min-h-7 px-3 max-md:py-1',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n size: LinkSize.MD,\n class: 'min-h-8 px-6 max-md:py-2',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n size: LinkSize.LG,\n class: 'min-h-10 px-8 max-md:py-3',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n size: LinkSize.XL,\n class: 'min-h-11 px-10 max-md:py-4',\n },\n // Ring color variants for button (Chrome bug fix: ring-current/20 doesn't work properly)\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.PRIMARY,\n class: 'ring-primary/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.SECONDARY,\n class: 'ring-secondary/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.DESTRUCTIVE,\n class: 'ring-destructive/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.NEUTRAL,\n class: 'ring-neutral/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.LIGHT,\n class: 'ring-white/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.DARK,\n class: 'ring-neutral-800/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.TEXT,\n class: 'ring-text/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.TEXT_INVERSE,\n class: 'ring-text-opposite/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.ERROR,\n class: 'ring-error/20',\n },\n {\n variant: [LinkVariant.BUTTON, LinkVariant.BUTTON_OUTLINED],\n color: LinkColor.SUCCESS,\n class: 'ring-success/20',\n },\n ],\n\n defaultVariants: {\n variant: LinkVariant.DEFAULT,\n color: LinkColor.PRIMARY,\n roundedSize: LinkRoundedSize.MD,\n underlined: LinkUnderlined.DEFAULT,\n size: LinkSize.MD,\n },\n }\n);\n\n/**\n * Props interface for the Link component\n *\n * @interface LinkProps\n * @extends {DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>}\n * @extends {VariantProps<typeof linkVariants>}\n */\nexport type LinkProps = DetailedHTMLProps<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n HTMLAnchorElement\n> &\n VariantProps<typeof linkVariants> & {\n /**\n * Accessible label for screen readers (required)\n * Provides context about what the link does or where it leads\n * @example \"Navigate to home page\"\n */\n label: string;\n\n /**\n * Whether this link opens in a new tab/window\n * When true, adds target=\"_blank\" and security attributes\n * Auto-detected for URLs starting with http/https when undefined\n * @default undefined (auto-detect based on href)\n */\n isExternalLink?: boolean;\n\n /**\n * If a link is a page section as '#id'\n * @default false\n */\n isPageSection?: boolean;\n\n /**\n * Whether this link represents the current page/active state\n * Adds aria-current=\"page\" for accessibility\n * @default false\n */\n isActive?: boolean;\n\n /**\n * Locale for internationalized URLs\n * When provided, URLs are automatically localized using Intlayer\n * @example 'fr', 'es', 'en'\n */\n locale?: LocalesValues;\n };\n\n/**\n * Utility function to determine if a link should be treated as external\n *\n * @param props - Link component props containing href and isExternalLink\n * @returns {boolean} True if the link should open externally\n *\n * @example\n * ```tsx\n * checkIsExternalLink({ href: '[https://example.com](https://example.com)' }) // true\n * checkIsExternalLink({ href: '/internal-page' }) // false\n * checkIsExternalLink({ href: '/page', isExternalLink: true }) // true\n * ```\n */\nexport const checkIsExternalLink = ({\n href,\n isExternalLink: isExternalLinkProp,\n}: LinkProps): boolean => {\n const isValidHref = typeof href === 'string' && href.trim() !== '';\n const isExternalLink =\n isExternalLinkProp === true ||\n (typeof isExternalLinkProp === 'undefined' &&\n isValidHref &&\n /^https?:\\/\\//.test(href));\n\n return isExternalLink;\n};\n\n/**\n * Link Component\n *\n * A versatile link component that handles both internal and external navigation\n * with comprehensive internationalization support and multiple visual variants.\n * ...\n */\nexport const Link: FC<LinkProps> = (props) => {\n const {\n variant = LinkVariant.DEFAULT,\n color = LinkColor.PRIMARY,\n roundedSize,\n children,\n label,\n className,\n isActive,\n underlined,\n locale,\n size,\n isExternalLink: isExternalLinkProp,\n isPageSection: isPageSectionProp,\n href: hrefProp,\n ...otherProps\n } = props;\n\n const isExternalLink = isExternalLinkProp ?? checkIsExternalLink(props);\n const isPageSection = isPageSectionProp ?? hrefProp?.startsWith('#') ?? false;\n\n const isChildrenString = typeof children === 'string';\n\n const rel = isExternalLink ? 'noopener noreferrer nofollow' : undefined;\n\n const target = isExternalLink ? '_blank' : '_self';\n\n const href =\n locale && hrefProp && !isExternalLink && !isPageSection\n ? getLocalizedUrl(hrefProp, locale)\n : hrefProp;\n\n return (\n <a\n href={href}\n aria-label={label}\n rel={rel}\n target={target}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n linkVariants({\n variant,\n color,\n roundedSize,\n underlined,\n size,\n className,\n })\n )}\n {...otherProps}\n >\n {children}\n\n {isExternalLink && isChildrenString && (\n <ExternalLink className=\"ml-2 inline-block size-4\" />\n )}\n {isPageSection && <MoveRight className=\"ml-2 inline-block size-4\" />}\n </a>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAYA,IAAY,sDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;;;AAQF,IAAY,kDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;;;AAIF,IAAY,8DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAGF,IAAY,gDAAL;AACL;AACA;AACA;AACA;AACA;;;;;;;;AAQF,IAAY,4DAAL;;AAEL;;AAEA;;AAEA;;;;;;;AAOF,MAAa,eAAe,IAC1B,iHACA;CACE,UAAU;EACR,SAAS;IACN,GAAG,YAAY,YACd;IACD,GAAG,YAAY,mBACd;IAED,GAAG,YAAY,WACd;IAED,GAAG,YAAY,oBACd;IAED,GAAG,YAAY,cACd;GACH;EACD,aAAa;IACV,GAAG,gBAAgB,SAAS;IAC5B,GAAG,gBAAgB,OAClB;IACD,GAAG,gBAAgB,OAClB;IACD,GAAG,gBAAgB,OAClB;IACD,GAAG,gBAAgB,OAClB;IACD,GAAG,gBAAgB,WAClB;IACD,GAAG,gBAAgB,aAClB;IACD,GAAG,gBAAgB,SAAS;GAC9B;EACD,OAAO;IACJ,GAAG,UAAU,YAAY;IACzB,GAAG,UAAU,cAAc;IAC3B,GAAG,UAAU,gBAAgB;IAC7B,GAAG,UAAU,YAAY;IACzB,GAAG,UAAU,UAAU;IACvB,GAAG,UAAU,SAAS;IACtB,GAAG,UAAU,SAAS;IACtB,GAAG,UAAU,iBAAiB;IAC9B,GAAG,UAAU,UAAU;IACvB,GAAG,UAAU,YAAY;IACzB,GAAG,UAAU,WAAW;GAC1B;EACD,MAAM;IACH,GAAG,SAAS,OAAO;IACnB,GAAG,SAAS,OAAO;IACnB,GAAG,SAAS,OAAO;IACnB,GAAG,SAAS,OAAO;IACnB,GAAG,SAAS,WAAW;GACzB;EACD,YAAY;IACT,eAAe,UAAU;IACzB,eAAe,OAAO;IACtB,eAAe,QAAQ;GACzB;EACF;CAED,kBAAkB;EAEhB;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,MAAM,SAAS;GACf,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,MAAM,SAAS;GACf,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,MAAM,SAAS;GACf,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,MAAM,SAAS;GACf,OAAO;GACR;EAED;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACD;GACE,SAAS,CAAC,YAAY,QAAQ,YAAY,gBAAgB;GAC1D,OAAO,UAAU;GACjB,OAAO;GACR;EACF;CAED,iBAAiB;EACf,SAAS,YAAY;EACrB,OAAO,UAAU;EACjB,aAAa,gBAAgB;EAC7B,YAAY,eAAe;EAC3B,MAAM,SAAS;EAChB;CACF,CACF;;;;;;;;;;;;;;AA+DD,MAAa,uBAAuB,EAClC,MACA,gBAAgB,yBACQ;CACxB,MAAM,cAAc,OAAO,SAAS,YAAY,KAAK,MAAM,KAAK;AAOhE,QALE,uBAAuB,QACtB,OAAO,uBAAuB,eAC7B,eACA,eAAe,KAAK,KAAK;;;;;;;;;AAY/B,MAAaA,QAAuB,UAAU;CAC5C,MAAM,EACJ,UAAU,YAAY,SACtB,QAAQ,UAAU,SAClB,aACA,UACA,OACA,WACA,UACA,YACA,QACA,MACA,gBAAgB,oBAChB,eAAe,mBACf,MAAM,UACN,GAAG,eACD;CAEJ,MAAM,iBAAiB,sBAAsB,oBAAoB,MAAM;CACvE,MAAM,gBAAgB,qBAAqB,UAAU,WAAW,IAAI,IAAI;CAExE,MAAM,mBAAmB,OAAO,aAAa;CAE7C,MAAM,MAAM,iBAAiB,iCAAiC;CAE9D,MAAM,SAAS,iBAAiB,WAAW;AAO3C,QACE,qBAAC;EACC,MANF,UAAU,YAAY,CAAC,kBAAkB,CAAC,gBACtC,gBAAgB,UAAU,OAAO,GACjC;EAKF,cAAY;EACP;EACG;EACR,gBAAc,WAAW,SAAS;EAClC,WAAW,GACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,CACH;EACD,GAAI;;GAEH;GAEA,kBAAkB,oBACjB,oBAAC,gBAAa,WAAU,6BAA6B;GAEtD,iBAAiB,oBAAC,aAAU,WAAU,6BAA6B;;GAClE"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Link, LinkColor, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants } from "./Link.mjs";
|
|
1
|
+
import { Link, LinkColor, LinkRoundedSize, LinkSize, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants } from "./Link.mjs";
|
|
2
2
|
|
|
3
|
-
export { Link, LinkColor, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants };
|
|
3
|
+
export { Link, LinkColor, LinkRoundedSize, LinkSize, LinkUnderlined, LinkVariant, checkIsExternalLink, linkVariants };
|
|
@@ -80,7 +80,7 @@ const DesktopNavbar = ({ logo, sections, rightItems, selectedChoice }) => /* @__
|
|
|
80
80
|
logo,
|
|
81
81
|
/* @__PURE__ */ jsx(TabSelector, {
|
|
82
82
|
selectedChoice,
|
|
83
|
-
className: "ml-[2vw] gap-3 overflow-x-auto text-neutral tracking-wide lg:ml-[5vw] lg:gap-3 xl:ml-[10vw] xl:gap-6",
|
|
83
|
+
className: "ml-[2vw] h-auto gap-3 overflow-x-auto text-neutral tracking-wide lg:ml-[5vw] lg:gap-3 xl:ml-[10vw] xl:gap-6",
|
|
84
84
|
tabs: sections,
|
|
85
85
|
hoverable: true,
|
|
86
86
|
color: TabSelectorColor.TEXT
|