@beautinique/frontend-components 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +49 -1
  2. package/dist/lib/button.d.cts +4 -1
  3. package/dist/lib/button.d.cts.map +1 -1
  4. package/dist/lib/button.d.ts +4 -1
  5. package/dist/lib/button.d.ts.map +1 -1
  6. package/dist/lib/constants.d.cts +4 -0
  7. package/dist/lib/constants.d.cts.map +1 -1
  8. package/dist/lib/constants.d.ts +4 -0
  9. package/dist/lib/constants.d.ts.map +1 -1
  10. package/dist/lib/tooltip.d.cts +36 -4
  11. package/dist/lib/tooltip.d.cts.map +1 -1
  12. package/dist/lib/tooltip.d.ts +36 -4
  13. package/dist/lib/tooltip.d.ts.map +1 -1
  14. package/dist/toast/ToastContainer.d.cts +1 -1
  15. package/dist/toast/ToastContainer.d.cts.map +1 -1
  16. package/dist/toast/ToastContainer.d.ts +1 -1
  17. package/dist/toast/ToastContainer.d.ts.map +1 -1
  18. package/dist/toast/index.cjs +37 -43
  19. package/dist/toast/index.cjs.map +1 -1
  20. package/dist/toast/index.js +24 -30
  21. package/dist/toast/index.js.map +1 -1
  22. package/dist/toast/types.d.cts +2 -2
  23. package/dist/toast/types.d.cts.map +1 -1
  24. package/dist/toast/types.d.ts +2 -2
  25. package/dist/toast/types.d.ts.map +1 -1
  26. package/dist/types/component.d.cts +22 -4
  27. package/dist/types/component.d.cts.map +1 -1
  28. package/dist/types/component.d.ts +22 -4
  29. package/dist/types/component.d.ts.map +1 -1
  30. package/dist/ui/Button.d.cts +1 -1
  31. package/dist/ui/Button.d.cts.map +1 -1
  32. package/dist/ui/Button.d.ts +1 -1
  33. package/dist/ui/Button.d.ts.map +1 -1
  34. package/dist/ui/Tooltip.d.cts +1 -1
  35. package/dist/ui/Tooltip.d.cts.map +1 -1
  36. package/dist/ui/Tooltip.d.ts +1 -1
  37. package/dist/ui/Tooltip.d.ts.map +1 -1
  38. package/dist/ui/index.cjs +144 -62
  39. package/dist/ui/index.cjs.map +1 -1
  40. package/dist/ui/index.js +144 -62
  41. package/dist/ui/index.js.map +1 -1
  42. package/package.json +2 -1
package/dist/ui/index.cjs CHANGED
@@ -1,61 +1,56 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('@iconify/react');
4
- var react$1 = require('react');
4
+ var tailwindMerge = require('tailwind-merge');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var react$1 = require('react');
6
7
  var reactDom = require('react-dom');
7
8
 
8
9
  var __defProp = Object.defineProperty;
9
10
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
11
 
11
12
  // src/lib/button.ts
12
- var getButtonCss = /* @__PURE__ */ __name((pattern) => {
13
- switch (pattern) {
14
- case "primary":
15
- return "text-white bg-sky-blue-burst shadow-primary-btn hover:shadow-primary-btn-hover";
16
- case "secondary":
17
- return "text-secondary-invert bg-secondary shadow-secondary-btn hover:shadow-secondary-btn-hover";
18
- case "tertiary":
19
- return "text-tertiary-invert bg-tertiary shadow-tertiary-btn hover:shadow-tertiary-btn-hover";
20
- case "outline":
21
- return "text-primary border border-primary shadow-outline-btn hover:shadow-outline-btn-hover";
22
- case "transparent":
23
- default:
24
- return "bg-transparent border border-primary/30 text-secondary";
25
- }
26
- }, "getButtonCss");
13
+ var PATTERN_CSS = {
14
+ primary: "text-white bg-sky-blue-burst shadow-primary-btn hover:shadow-primary-btn-hover",
15
+ secondary: "text-secondary-invert bg-secondary shadow-secondary-btn hover:shadow-secondary-btn-hover",
16
+ tertiary: "text-tertiary-invert bg-tertiary shadow-tertiary-btn hover:shadow-tertiary-btn-hover",
17
+ outline: "text-primary border border-primary shadow-outline-btn hover:shadow-outline-btn-hover",
18
+ transparent: "bg-transparent border border-primary/30 text-secondary"
19
+ };
20
+ var getButtonCss = /* @__PURE__ */ __name((pattern) => Object.hasOwn(PATTERN_CSS, pattern) ? PATTERN_CSS[pattern] : PATTERN_CSS.transparent, "getButtonCss");
21
+ var BASE_CSS = "group flex w-full cursor-pointer items-center justify-center gap-1 rounded-lg px-4 py-3 text-sm leading-4 transition-all duration-300 disabled:cursor-not-allowed disabled:opacity-85 lg:px-5 lg:py-4 xl:text-base";
27
22
  var Button = /* @__PURE__ */ __name(({
28
23
  pattern,
29
24
  content,
30
25
  className = "",
31
26
  leftIcon,
32
27
  rightIcon,
33
- buttonProps
34
- }) => {
35
- const btnCSS = getButtonCss(pattern);
36
- const handleClick = /* @__PURE__ */ __name((e) => {
37
- if (buttonProps?.disabled) return;
38
- buttonProps?.onClick?.(e);
39
- }, "handleClick");
40
- return /* @__PURE__ */ jsxRuntime.jsxs(
28
+ buttonProps,
29
+ ref
30
+ }) => (
31
+ // A class that is given wins over the one it clashes with (`rounded-md` over `rounded-lg`),
32
+ // with no `!`; one for another screen size (`lg:px-5`) is a different class and stays.
33
+ /* @__PURE__ */ jsxRuntime.jsxs(
41
34
  "button",
42
35
  {
43
36
  ...buttonProps,
44
- className: `group flex w-full cursor-pointer items-center justify-center gap-1 rounded-lg px-4 py-3 text-sm leading-4 transition-all duration-300 disabled:cursor-not-allowed disabled:opacity-85 lg:px-5 lg:py-4 xl:text-base ${btnCSS} ${className} ${buttonProps?.className ?? ""}`,
37
+ ref,
38
+ className: tailwindMerge.twMerge(BASE_CSS, getButtonCss(pattern), className, buttonProps?.className),
45
39
  type: buttonProps?.type ?? "button",
46
- onClick: handleClick,
47
40
  children: [
48
41
  leftIcon && /* @__PURE__ */ jsxRuntime.jsx(react.Icon, { ...leftIcon }),
49
42
  typeof content === "object" ? /* @__PURE__ */ jsxRuntime.jsx(react.Icon, { ...content }) : content,
50
43
  rightIcon && /* @__PURE__ */ jsxRuntime.jsx(react.Icon, { ...rightIcon })
51
44
  ]
52
45
  }
53
- );
54
- }, "Button");
46
+ )
47
+ ), "Button");
55
48
 
56
49
  // src/lib/constants.ts
57
50
  var TOOLTIP_GAP = 15;
58
51
  var TOOLTIP_ANIMATION_DURATION = 400;
52
+ var TOOLTIP_VIEWPORT_MARGIN = 8;
53
+ var TOOLTIP_ARROW_INSET = 16;
59
54
  var TOAST_TYPES = [
60
55
  "success",
61
56
  "error",
@@ -69,6 +64,13 @@ var TOAST_TYPES = [
69
64
  Object.fromEntries(TOAST_TYPES.map((type) => [type, type]));
70
65
 
71
66
  // src/lib/tooltip.ts
67
+ var OPPOSITE = {
68
+ top: "bottom",
69
+ bottom: "top",
70
+ left: "right",
71
+ right: "left"
72
+ };
73
+ var clamp = /* @__PURE__ */ __name((value, min, max) => Math.min(Math.max(value, min), max), "clamp");
72
74
  var getTooltipPosition = /* @__PURE__ */ __name((rect, placement) => {
73
75
  switch (placement) {
74
76
  case "bottom":
@@ -82,6 +84,45 @@ var getTooltipPosition = /* @__PURE__ */ __name((rect, placement) => {
82
84
  return { top: rect.top, left: rect.left + rect.width / 2 };
83
85
  }
84
86
  }, "getTooltipPosition");
87
+ var fitsOn = /* @__PURE__ */ __name((rect, size, placement, viewport) => {
88
+ switch (placement) {
89
+ case "bottom":
90
+ return rect.bottom + TOOLTIP_GAP + size.height <= viewport.height - TOOLTIP_VIEWPORT_MARGIN;
91
+ case "right":
92
+ return rect.right + TOOLTIP_GAP + size.width <= viewport.width - TOOLTIP_VIEWPORT_MARGIN;
93
+ case "left":
94
+ return rect.left - TOOLTIP_GAP - size.width >= TOOLTIP_VIEWPORT_MARGIN;
95
+ case "top":
96
+ default:
97
+ return rect.top - TOOLTIP_GAP - size.height >= TOOLTIP_VIEWPORT_MARGIN;
98
+ }
99
+ }, "fitsOn");
100
+ var getTooltipLayout = /* @__PURE__ */ __name(({
101
+ rect,
102
+ placement,
103
+ size,
104
+ viewport
105
+ }) => {
106
+ if (!size) return { placement, position: getTooltipPosition(rect, placement), arrowShift: 0 };
107
+ const side = !fitsOn(rect, size, placement, viewport) && fitsOn(rect, size, OPPOSITE[placement], viewport) ? OPPOSITE[placement] : placement;
108
+ const position = getTooltipPosition(rect, side);
109
+ const isHorizontal = side === "top" || side === "bottom";
110
+ const length = isHorizontal ? size.width : size.height;
111
+ const limit = isHorizontal ? viewport.width : viewport.height;
112
+ const start = (isHorizontal ? position.left : position.top) - length / 2;
113
+ const min = TOOLTIP_VIEWPORT_MARGIN;
114
+ const max = limit - TOOLTIP_VIEWPORT_MARGIN - length;
115
+ const shift = max < min ? 0 : clamp(start, min, max) - start;
116
+ const reach = Math.max(0, length / 2 - TOOLTIP_ARROW_INSET);
117
+ const arrowShift = clamp(-shift, -reach, reach);
118
+ return {
119
+ placement: side,
120
+ position: isHorizontal ? { top: position.top, left: position.left + shift } : { top: position.top + shift, left: position.left },
121
+ arrowShift: arrowShift === 0 ? 0 : arrowShift
122
+ // never -0
123
+ };
124
+ }, "getTooltipLayout");
125
+ var isSameTooltipLayout = /* @__PURE__ */ __name((a, b) => a.placement === b.placement && a.arrowShift === b.arrowShift && a.position.top === b.position.top && a.position.left === b.position.left, "isSameTooltipLayout");
85
126
  var getTooltipTransform = /* @__PURE__ */ __name((placement, isVisible) => {
86
127
  const gap = String(isVisible ? TOOLTIP_GAP : 0);
87
128
  switch (placement) {
@@ -99,90 +140,134 @@ var getTooltipTransform = /* @__PURE__ */ __name((placement, isVisible) => {
99
140
  var getTooltipArrowCss = /* @__PURE__ */ __name((placement) => {
100
141
  switch (placement) {
101
142
  case "bottom":
102
- return "after:bottom-full after:left-1/2 after:-translate-x-1/2 after:border-b-silver-jet-2";
143
+ return "after:bottom-full after:left-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:-translate-x-1/2 after:border-b-silver-jet-2";
103
144
  case "right":
104
- return "after:top-1/2 after:right-full after:-translate-y-1/2 after:border-r-silver-jet-2";
145
+ return "after:top-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:right-full after:-translate-y-1/2 after:border-r-silver-jet-2";
105
146
  case "left":
106
- return "after:top-1/2 after:left-full after:-translate-y-1/2 after:border-l-silver-jet-2";
147
+ return "after:top-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:left-full after:-translate-y-1/2 after:border-l-silver-jet-2";
107
148
  case "top":
108
149
  default:
109
- return "after:top-full after:left-1/2 after:-translate-x-1/2 after:border-t-silver-jet-2";
150
+ return "after:top-full after:left-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:-translate-x-1/2 after:border-t-silver-jet-2";
110
151
  }
111
152
  }, "getTooltipArrowCss");
112
- var OriginalTooltip = /* @__PURE__ */ __name(({
153
+ var Tooltip = /* @__PURE__ */ __name(({
113
154
  title,
114
155
  description,
115
156
  children,
116
157
  className = "",
117
158
  containerClassName = "",
118
- placement = "top"
159
+ placement = "top",
160
+ required = true
119
161
  }) => {
162
+ const id = react$1.useId();
120
163
  const containerRef = react$1.useRef(null);
164
+ const tooltipRef = react$1.useRef(null);
121
165
  const [isOpen, setIsOpen] = react$1.useState(false);
122
166
  const [isVisible, setIsVisible] = react$1.useState(false);
123
- const [tooltipStyle, setTooltipStyle] = react$1.useState();
167
+ const [layout, setLayout] = react$1.useState();
124
168
  const closeTimerRef = react$1.useRef(void 0);
125
169
  const animationFrameRef = react$1.useRef(void 0);
126
- const updateTooltipPosition = react$1.useCallback(() => {
170
+ const updateLayout = react$1.useCallback(() => {
127
171
  const container = containerRef.current;
128
172
  if (!container) return;
129
- setTooltipStyle(getTooltipPosition(container.getBoundingClientRect(), placement));
130
- }, [placement]);
131
- const showTooltip = /* @__PURE__ */ __name(() => {
132
- animationFrameRef.current = window.requestAnimationFrame(() => {
133
- animationFrameRef.current = window.requestAnimationFrame(() => {
134
- setIsVisible(true);
135
- });
173
+ const next = getTooltipLayout({
174
+ rect: container.getBoundingClientRect(),
175
+ placement,
176
+ size: tooltipRef.current?.getBoundingClientRect(),
177
+ viewport: { width: window.innerWidth, height: window.innerHeight }
136
178
  });
137
- }, "showTooltip");
179
+ setLayout((current) => current && isSameTooltipLayout(current, next) ? current : next);
180
+ }, [placement]);
138
181
  const openTooltip = /* @__PURE__ */ __name(() => {
182
+ if (!required) return;
139
183
  if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);
140
184
  if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);
141
- updateTooltipPosition();
185
+ updateLayout();
142
186
  setIsVisible(false);
143
187
  setIsOpen(true);
144
- showTooltip();
188
+ animationFrameRef.current = window.requestAnimationFrame(() => {
189
+ animationFrameRef.current = window.requestAnimationFrame(() => {
190
+ setIsVisible(true);
191
+ });
192
+ });
145
193
  }, "openTooltip");
146
- const closeTooltip = /* @__PURE__ */ __name(() => {
194
+ const closeTooltip = react$1.useCallback(() => {
147
195
  if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);
196
+ if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);
148
197
  setIsVisible(false);
149
198
  closeTimerRef.current = window.setTimeout(() => {
150
199
  setIsOpen(false);
151
200
  }, TOOLTIP_ANIMATION_DURATION);
152
- }, "closeTooltip");
201
+ }, []);
153
202
  react$1.useEffect(() => {
154
203
  return () => {
155
204
  if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);
156
205
  if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);
157
206
  };
158
207
  }, []);
208
+ react$1.useEffect(() => {
209
+ if (!isOpen) return;
210
+ const closeOnEscape = /* @__PURE__ */ __name((event) => {
211
+ if (event.key === "Escape") closeTooltip();
212
+ }, "closeOnEscape");
213
+ document.addEventListener("keydown", closeOnEscape);
214
+ return () => {
215
+ document.removeEventListener("keydown", closeOnEscape);
216
+ };
217
+ }, [isOpen, closeTooltip]);
159
218
  react$1.useLayoutEffect(() => {
160
219
  if (!isOpen) return;
161
- updateTooltipPosition();
162
- window.addEventListener("resize", updateTooltipPosition);
163
- window.addEventListener("scroll", updateTooltipPosition, true);
220
+ updateLayout();
221
+ let frame;
222
+ const scheduleUpdate = /* @__PURE__ */ __name(() => {
223
+ if (frame) return;
224
+ frame = window.requestAnimationFrame(() => {
225
+ frame = void 0;
226
+ updateLayout();
227
+ });
228
+ }, "scheduleUpdate");
229
+ window.addEventListener("resize", scheduleUpdate);
230
+ window.addEventListener("scroll", scheduleUpdate, true);
164
231
  return () => {
165
- window.removeEventListener("resize", updateTooltipPosition);
166
- window.removeEventListener("scroll", updateTooltipPosition, true);
232
+ window.removeEventListener("resize", scheduleUpdate);
233
+ window.removeEventListener("scroll", scheduleUpdate, true);
234
+ if (frame) window.cancelAnimationFrame(frame);
167
235
  };
168
- }, [isOpen, updateTooltipPosition]);
236
+ }, [isOpen, updateLayout]);
237
+ const trigger = react$1.useMemo(() => {
238
+ if (!required || !isOpen || !react$1.isValidElement(children)) {
239
+ return children;
240
+ }
241
+ const describedBy = children.props["aria-describedby"];
242
+ return react$1.cloneElement(children, {
243
+ "aria-describedby": describedBy ? `${describedBy} ${id}` : id
244
+ });
245
+ }, [children, id, isOpen, required]);
246
+ const isShown = required && isOpen && layout !== void 0;
169
247
  return /* @__PURE__ */ jsxRuntime.jsxs(
170
248
  "div",
171
249
  {
172
250
  ref: containerRef,
173
- className: `relative inline-flex ${containerClassName}`,
251
+ className: required ? `relative inline-flex ${containerClassName}` : "contents",
174
252
  onBlur: closeTooltip,
175
253
  onFocus: openTooltip,
176
254
  onMouseEnter: openTooltip,
177
255
  onMouseLeave: closeTooltip,
178
256
  children: [
179
- children,
180
- isOpen && tooltipStyle && reactDom.createPortal(
257
+ trigger,
258
+ isShown && reactDom.createPortal(
181
259
  /* @__PURE__ */ jsxRuntime.jsxs(
182
260
  "div",
183
261
  {
184
- className: `border-silver-jet-2 bg-platinum-jet fixed z-9999 flex flex-col items-center justify-center rounded-lg border px-3 py-2 text-center whitespace-nowrap backdrop-blur-md transition-all duration-400 ease-in-out after:absolute after:border-8 after:border-solid after:border-transparent after:content-[''] ${isVisible ? "visible opacity-100" : "invisible opacity-0"} ${getTooltipArrowCss(placement)} ${className} `,
185
- style: { ...tooltipStyle, transform: getTooltipTransform(placement, isVisible) },
262
+ ref: tooltipRef,
263
+ id,
264
+ role: "tooltip",
265
+ className: `border-silver-jet-2 bg-platinum-jet fixed z-9999 flex flex-col items-center justify-center rounded-lg border px-3 py-2 text-center whitespace-nowrap backdrop-blur-md transition-all duration-400 ease-in-out after:absolute after:border-8 after:border-solid after:border-transparent after:content-[''] ${isVisible ? "visible opacity-100" : "invisible opacity-0"} ${getTooltipArrowCss(layout.placement)} ${className} `,
266
+ style: {
267
+ ...layout.position,
268
+ "--tooltip-arrow-shift": `${String(layout.arrowShift)}px`,
269
+ transform: getTooltipTransform(layout.placement, isVisible)
270
+ },
186
271
  children: [
187
272
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-primary text-xs font-medium", children: title }),
188
273
  description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-tertiary text-[10px] font-light", children: description })
@@ -194,9 +279,6 @@ var OriginalTooltip = /* @__PURE__ */ __name(({
194
279
  ]
195
280
  }
196
281
  );
197
- }, "OriginalTooltip");
198
- var Tooltip = /* @__PURE__ */ __name(({ children, required = true, ...props }) => {
199
- return required ? /* @__PURE__ */ jsxRuntime.jsx(OriginalTooltip, { ...props, children }) : children;
200
282
  }, "Tooltip");
201
283
 
202
284
  exports.Button = Button;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/button.ts","../../src/ui/Button.tsx","../../src/lib/constants.ts","../../src/lib/tooltip.ts","../../src/ui/Tooltip.tsx"],"names":["jsxs","jsx","Icon","useRef","useState","useCallback","useEffect","useLayoutEffect","createPortal"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { IButton } from '../types/component.js';\n\nexport const getButtonCss = (pattern: IButton['pattern']) => {\n switch (pattern) {\n case 'primary':\n return 'text-white bg-sky-blue-burst shadow-primary-btn hover:shadow-primary-btn-hover';\n case 'secondary':\n return 'text-secondary-invert bg-secondary shadow-secondary-btn hover:shadow-secondary-btn-hover';\n case 'tertiary':\n return 'text-tertiary-invert bg-tertiary shadow-tertiary-btn hover:shadow-tertiary-btn-hover';\n case 'outline':\n return 'text-primary border border-primary shadow-outline-btn hover:shadow-outline-btn-hover';\n case 'transparent':\n default:\n return 'bg-transparent border border-primary/30 text-secondary';\n }\n};\n","import { Icon } from '@iconify/react';\nimport { type MouseEvent } from 'react';\n\nimport { getButtonCss } from '../lib/button.js';\nimport type { IButton } from '../types/component.js';\n\nexport const Button = ({\n pattern,\n content,\n className = '',\n leftIcon,\n rightIcon,\n buttonProps,\n}: IButton) => {\n const btnCSS = getButtonCss(pattern);\n\n const handleClick = (e: MouseEvent<HTMLButtonElement>) => {\n if (buttonProps?.disabled) return;\n buttonProps?.onClick?.(e);\n };\n\n return (\n <button\n {...buttonProps}\n className={`group flex w-full cursor-pointer items-center justify-center gap-1 rounded-lg px-4 py-3 text-sm leading-4 transition-all duration-300 disabled:cursor-not-allowed disabled:opacity-85 lg:px-5 lg:py-4 xl:text-base ${btnCSS} ${className} ${\n buttonProps?.className ?? ''\n }`}\n type={buttonProps?.type ?? 'button'}\n onClick={handleClick}\n >\n {leftIcon && <Icon {...leftIcon} />}\n {typeof content === 'object' ? <Icon {...content} /> : content}\n {rightIcon && <Icon {...rightIcon} />}\n </button>\n );\n};\n","export const TOOLTIP_GAP = 15 as const;\nexport const TOOLTIP_ANIMATION_DURATION = 400 as const;\n\nexport const TOAST_TYPES = [\n 'success',\n 'error',\n 'warning',\n 'progress',\n 'uploads',\n 'loading',\n 'default',\n 'custom',\n] as const;\n\nexport const TOAST_TYPE = Object.fromEntries(TOAST_TYPES.map((type) => [type, type])) as {\n [K in (typeof TOAST_TYPES)[number]]: K;\n};\n","import type { CSSProperties } from 'react';\n\nimport type { ITooltip } from '../types/component.js';\nimport { TOOLTIP_GAP } from './constants.js';\n\nexport const getTooltipPosition = (\n rect: DOMRect,\n placement: NonNullable<ITooltip['placement']>,\n): CSSProperties => {\n switch (placement) {\n case 'bottom':\n return { top: rect.bottom, left: rect.left + rect.width / 2 };\n case 'right':\n return { top: rect.top + rect.height / 2, left: rect.right };\n case 'left':\n return { top: rect.top + rect.height / 2, left: rect.left };\n case 'top':\n default:\n return { top: rect.top, left: rect.left + rect.width / 2 };\n }\n};\n\nexport const getTooltipTransform = (\n placement: NonNullable<ITooltip['placement']>,\n isVisible: boolean,\n) => {\n const gap = String(isVisible ? TOOLTIP_GAP : 0);\n\n switch (placement) {\n case 'bottom':\n return `translate(-50%, ${gap}px)`;\n case 'right':\n return `translate(${gap}px, -50%)`;\n case 'left':\n return `translate(calc(-100% - ${gap}px), -50%)`;\n case 'top':\n default:\n return `translate(-50%, calc(-100% - ${gap}px))`;\n }\n};\n\nexport const getTooltipArrowCss = (placement: NonNullable<ITooltip['placement']>) => {\n switch (placement) {\n case 'bottom':\n return 'after:bottom-full after:left-1/2 after:-translate-x-1/2 after:border-b-silver-jet-2';\n case 'right':\n return 'after:top-1/2 after:right-full after:-translate-y-1/2 after:border-r-silver-jet-2';\n case 'left':\n return 'after:top-1/2 after:left-full after:-translate-y-1/2 after:border-l-silver-jet-2';\n case 'top':\n default:\n return 'after:top-full after:left-1/2 after:-translate-x-1/2 after:border-t-silver-jet-2';\n }\n};\n","import {\n type CSSProperties,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { TOOLTIP_ANIMATION_DURATION } from '../lib/constants.js';\nimport { getTooltipArrowCss, getTooltipPosition, getTooltipTransform } from '../lib/tooltip.js';\nimport type { ITooltip } from '../types/component.js';\n\nconst OriginalTooltip = ({\n title,\n description,\n children,\n className = '',\n containerClassName = '',\n placement = 'top',\n}: Omit<ITooltip, 'required'>) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const [tooltipStyle, setTooltipStyle] = useState<CSSProperties>();\n const closeTimerRef = useRef<number | undefined>(undefined);\n const animationFrameRef = useRef<number | undefined>(undefined);\n\n const updateTooltipPosition = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n setTooltipStyle(getTooltipPosition(container.getBoundingClientRect(), placement));\n }, [placement]);\n\n const showTooltip = () => {\n animationFrameRef.current = window.requestAnimationFrame(() => {\n animationFrameRef.current = window.requestAnimationFrame(() => {\n setIsVisible(true);\n });\n });\n };\n\n const openTooltip = () => {\n if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);\n if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);\n\n updateTooltipPosition();\n setIsVisible(false);\n setIsOpen(true);\n showTooltip();\n };\n\n const closeTooltip = () => {\n if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);\n\n setIsVisible(false);\n closeTimerRef.current = window.setTimeout(() => {\n setIsOpen(false);\n }, TOOLTIP_ANIMATION_DURATION);\n };\n\n useEffect(() => {\n return () => {\n if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);\n if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);\n };\n }, []);\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n updateTooltipPosition();\n window.addEventListener('resize', updateTooltipPosition);\n window.addEventListener('scroll', updateTooltipPosition, true);\n\n return () => {\n window.removeEventListener('resize', updateTooltipPosition);\n window.removeEventListener('scroll', updateTooltipPosition, true);\n };\n }, [isOpen, updateTooltipPosition]);\n\n return (\n <div\n ref={containerRef}\n className={`relative inline-flex ${containerClassName}`}\n onBlur={closeTooltip}\n onFocus={openTooltip}\n onMouseEnter={openTooltip}\n onMouseLeave={closeTooltip}\n >\n {/* Main Content */}\n {children}\n {/* Tooltip */}\n {isOpen &&\n tooltipStyle &&\n createPortal(\n <div\n className={`border-silver-jet-2 bg-platinum-jet fixed z-9999 flex flex-col items-center justify-center rounded-lg border px-3 py-2 text-center whitespace-nowrap backdrop-blur-md transition-all duration-400 ease-in-out after:absolute after:border-8 after:border-solid after:border-transparent after:content-[''] ${isVisible ? 'visible opacity-100' : 'invisible opacity-0'} ${getTooltipArrowCss(placement)} ${className} `}\n style={{ ...tooltipStyle, transform: getTooltipTransform(placement, isVisible) }}\n >\n <div className=\"text-primary text-xs font-medium\">{title}</div>\n {description && (\n <div className=\"text-tertiary text-[10px] font-light\">{description}</div>\n )}\n </div>,\n document.body,\n )}\n </div>\n );\n};\n\nexport const Tooltip = ({ children, required = true, ...props }: ITooltip) => {\n return required ? <OriginalTooltip {...props}>{children}</OriginalTooltip> : children;\n};\n"]}
1
+ {"version":3,"sources":["../../src/lib/button.ts","../../src/ui/Button.tsx","../../src/lib/constants.ts","../../src/lib/tooltip.ts","../../src/ui/Tooltip.tsx"],"names":["jsxs","twMerge","jsx","Icon","useId","useRef","useState","useCallback","useEffect","useLayoutEffect","useMemo","isValidElement","cloneElement","createPortal"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { IButton } from '../types/component.js';\n\ntype TPattern = IButton['pattern'];\n\n// A `Record`: adding a pattern to the type is a type error here until it has a look.\nconst PATTERN_CSS: Record<TPattern, string> = {\n primary: 'text-white bg-sky-blue-burst shadow-primary-btn hover:shadow-primary-btn-hover',\n secondary:\n 'text-secondary-invert bg-secondary shadow-secondary-btn hover:shadow-secondary-btn-hover',\n tertiary: 'text-tertiary-invert bg-tertiary shadow-tertiary-btn hover:shadow-tertiary-btn-hover',\n outline: 'text-primary border border-primary shadow-outline-btn hover:shadow-outline-btn-hover',\n transparent: 'bg-transparent border border-primary/30 text-secondary',\n};\n\n/** The look of a pattern; a pattern nobody knows (from code that is not type checked) is transparent. */\nexport const getButtonCss = (pattern: TPattern) =>\n Object.hasOwn(PATTERN_CSS, pattern) ? PATTERN_CSS[pattern] : PATTERN_CSS.transparent;\n","import { Icon } from '@iconify/react';\nimport { twMerge } from 'tailwind-merge';\n\nimport { getButtonCss } from '../lib/button.js';\nimport type { IButton } from '../types/component.js';\n\nconst BASE_CSS =\n 'group flex w-full cursor-pointer items-center justify-center gap-1 rounded-lg px-4 py-3 text-sm leading-4 transition-all duration-300 disabled:cursor-not-allowed disabled:opacity-85 lg:px-5 lg:py-4 xl:text-base';\n\nexport const Button = ({\n pattern,\n content,\n className = '',\n leftIcon,\n rightIcon,\n buttonProps,\n ref,\n}: IButton) => (\n // A class that is given wins over the one it clashes with (`rounded-md` over `rounded-lg`),\n // with no `!`; one for another screen size (`lg:px-5`) is a different class and stays.\n <button\n {...buttonProps}\n ref={ref}\n className={twMerge(BASE_CSS, getButtonCss(pattern), className, buttonProps?.className)}\n type={buttonProps?.type ?? 'button'}\n >\n {leftIcon && <Icon {...leftIcon} />}\n {typeof content === 'object' ? <Icon {...content} /> : content}\n {rightIcon && <Icon {...rightIcon} />}\n </button>\n);\n","export const TOOLTIP_GAP = 15 as const;\nexport const TOOLTIP_ANIMATION_DURATION = 400 as const;\n/** The least space a tooltip keeps between itself and the edge of the window. */\nexport const TOOLTIP_VIEWPORT_MARGIN = 8 as const;\n/** The arrow stays at least this far from the ends of the tooltip (its own size and the rounded corner). */\nexport const TOOLTIP_ARROW_INSET = 16 as const;\n\nexport const TOAST_TYPES = [\n 'success',\n 'error',\n 'warning',\n 'progress',\n 'uploads',\n 'loading',\n 'default',\n 'custom',\n] as const;\n\nexport const TOAST_TYPE = Object.fromEntries(TOAST_TYPES.map((type) => [type, type])) as {\n [K in (typeof TOAST_TYPES)[number]]: K;\n};\n","import type { ITooltip } from '../types/component.js';\nimport { TOOLTIP_ARROW_INSET, TOOLTIP_GAP, TOOLTIP_VIEWPORT_MARGIN } from './constants.js';\n\ntype TPlacement = NonNullable<ITooltip['placement']>;\n\nexport interface ISize {\n width: number;\n height: number;\n}\n\nexport interface ITooltipLayout {\n /** The side the tooltip really opens on: the opposite one when there is no room on the asked one. */\n placement: TPlacement;\n /** Where the tooltip points to, in the window (it is moved away from it by `getTooltipTransform`). */\n position: { top: number; left: number };\n /** How far the arrow moves along the tooltip to keep pointing at the content when the tooltip is pushed in. */\n arrowShift: number;\n}\n\nconst OPPOSITE: Record<TPlacement, TPlacement> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\nconst clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);\n\nexport const getTooltipPosition = (\n rect: DOMRect,\n placement: TPlacement,\n): { top: number; left: number } => {\n switch (placement) {\n case 'bottom':\n return { top: rect.bottom, left: rect.left + rect.width / 2 };\n case 'right':\n return { top: rect.top + rect.height / 2, left: rect.right };\n case 'left':\n return { top: rect.top + rect.height / 2, left: rect.left };\n case 'top':\n default:\n return { top: rect.top, left: rect.left + rect.width / 2 };\n }\n};\n\n/** Whether a tooltip of this size, on this side of the content, stays inside the window. */\nconst fitsOn = (rect: DOMRect, size: ISize, placement: TPlacement, viewport: ISize) => {\n switch (placement) {\n case 'bottom':\n return rect.bottom + TOOLTIP_GAP + size.height <= viewport.height - TOOLTIP_VIEWPORT_MARGIN;\n case 'right':\n return rect.right + TOOLTIP_GAP + size.width <= viewport.width - TOOLTIP_VIEWPORT_MARGIN;\n case 'left':\n return rect.left - TOOLTIP_GAP - size.width >= TOOLTIP_VIEWPORT_MARGIN;\n case 'top':\n default:\n return rect.top - TOOLTIP_GAP - size.height >= TOOLTIP_VIEWPORT_MARGIN;\n }\n};\n\n/**\n * Where the tooltip goes. Without its `size` (it is not on the page yet) it goes where it was asked\n * to. With it, it opens on the opposite side when the asked one has no room and the other has, and\n * it is pushed along that side to stay inside the window; the arrow keeps pointing at the content.\n */\nexport const getTooltipLayout = ({\n rect,\n placement,\n size,\n viewport,\n}: {\n rect: DOMRect;\n placement: TPlacement;\n size?: ISize;\n viewport: ISize;\n}): ITooltipLayout => {\n if (!size) return { placement, position: getTooltipPosition(rect, placement), arrowShift: 0 };\n\n const side =\n !fitsOn(rect, size, placement, viewport) && fitsOn(rect, size, OPPOSITE[placement], viewport)\n ? OPPOSITE[placement]\n : placement;\n const position = getTooltipPosition(rect, side);\n\n const isHorizontal = side === 'top' || side === 'bottom';\n const length = isHorizontal ? size.width : size.height;\n const limit = isHorizontal ? viewport.width : viewport.height;\n const start = (isHorizontal ? position.left : position.top) - length / 2;\n const min = TOOLTIP_VIEWPORT_MARGIN;\n const max = limit - TOOLTIP_VIEWPORT_MARGIN - length;\n // a tooltip bigger than the window cannot be pushed in, it is left where it is\n const shift = max < min ? 0 : clamp(start, min, max) - start;\n const reach = Math.max(0, length / 2 - TOOLTIP_ARROW_INSET);\n const arrowShift = clamp(-shift, -reach, reach);\n\n return {\n placement: side,\n position: isHorizontal\n ? { top: position.top, left: position.left + shift }\n : { top: position.top + shift, left: position.left },\n arrowShift: arrowShift === 0 ? 0 : arrowShift, // never -0\n };\n};\n\n/** Whether two layouts put the tooltip in the same place (so there is nothing to change). */\nexport const isSameTooltipLayout = (a: ITooltipLayout, b: ITooltipLayout) =>\n a.placement === b.placement &&\n a.arrowShift === b.arrowShift &&\n a.position.top === b.position.top &&\n a.position.left === b.position.left;\n\nexport const getTooltipTransform = (placement: TPlacement, isVisible: boolean) => {\n const gap = String(isVisible ? TOOLTIP_GAP : 0);\n\n switch (placement) {\n case 'bottom':\n return `translate(-50%, ${gap}px)`;\n case 'right':\n return `translate(${gap}px, -50%)`;\n case 'left':\n return `translate(calc(-100% - ${gap}px), -50%)`;\n case 'top':\n default:\n return `translate(-50%, calc(-100% - ${gap}px))`;\n }\n};\n\n// The arrow sits in the middle of its side plus `--tooltip-arrow-shift` (set by the tooltip).\nexport const getTooltipArrowCss = (placement: TPlacement) => {\n switch (placement) {\n case 'bottom':\n return 'after:bottom-full after:left-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:-translate-x-1/2 after:border-b-silver-jet-2';\n case 'right':\n return 'after:top-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:right-full after:-translate-y-1/2 after:border-r-silver-jet-2';\n case 'left':\n return 'after:top-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:left-full after:-translate-y-1/2 after:border-l-silver-jet-2';\n case 'top':\n default:\n return 'after:top-full after:left-[calc(50%_+_var(--tooltip-arrow-shift,0px))] after:-translate-x-1/2 after:border-t-silver-jet-2';\n }\n};\n","import {\n cloneElement,\n type CSSProperties,\n isValidElement,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { TOOLTIP_ANIMATION_DURATION } from '../lib/constants.js';\nimport {\n getTooltipArrowCss,\n getTooltipLayout,\n getTooltipTransform,\n isSameTooltipLayout,\n type ITooltipLayout,\n} from '../lib/tooltip.js';\nimport type { ITooltip } from '../types/component.js';\n\n/*\n * `required={false}` keeps the wrapper (as `display: contents`, so it takes no room) instead of\n * returning the content alone: the content then stays in the same place of the tree when `required`\n * changes, and keeps its state (an open select, a focused input) instead of being created again.\n */\nexport const Tooltip = ({\n title,\n description,\n children,\n className = '',\n containerClassName = '',\n placement = 'top',\n required = true,\n}: ITooltip) => {\n const id = useId();\n const containerRef = useRef<HTMLDivElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const [layout, setLayout] = useState<ITooltipLayout>();\n const closeTimerRef = useRef<number | undefined>(undefined);\n const animationFrameRef = useRef<number | undefined>(undefined);\n\n // Places the tooltip from where the content is now and, once the tooltip is on the page, from its\n // size too, so it can open on the other side or be pushed in when it would leave the window.\n const updateLayout = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const next = getTooltipLayout({\n rect: container.getBoundingClientRect(),\n placement,\n size: tooltipRef.current?.getBoundingClientRect(),\n viewport: { width: window.innerWidth, height: window.innerHeight },\n });\n\n // the same place again: no new state, so no render\n setLayout((current) => (current && isSameTooltipLayout(current, next) ? current : next));\n }, [placement]);\n\n const openTooltip = () => {\n if (!required) return;\n\n if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);\n if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);\n\n updateLayout();\n setIsVisible(false);\n setIsOpen(true);\n // two frames, so the browser has drawn it hidden first and the fade has something to start from\n animationFrameRef.current = window.requestAnimationFrame(() => {\n animationFrameRef.current = window.requestAnimationFrame(() => {\n setIsVisible(true);\n });\n });\n };\n\n const closeTooltip = useCallback(() => {\n if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);\n // Without this, a close that was asked twice (the mouse leaves, then the focus does) leaves the\n // first timer running with nobody holding it: it closes a tooltip the mouse has come back to.\n if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);\n\n setIsVisible(false);\n closeTimerRef.current = window.setTimeout(() => {\n setIsOpen(false);\n }, TOOLTIP_ANIMATION_DURATION);\n }, []);\n\n useEffect(() => {\n return () => {\n if (closeTimerRef.current) window.clearTimeout(closeTimerRef.current);\n if (animationFrameRef.current) window.cancelAnimationFrame(animationFrameRef.current);\n };\n }, []);\n\n // Escape closes it, as a person who cannot move the mouse away has no other way to (WCAG 1.4.13).\n useEffect(() => {\n if (!isOpen) return;\n\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') closeTooltip();\n };\n\n document.addEventListener('keydown', closeOnEscape);\n\n return () => {\n document.removeEventListener('keydown', closeOnEscape);\n };\n }, [isOpen, closeTooltip]);\n\n // Follows the content while the window is resized or scrolled: at most once a frame, however\n // many scroll events there are.\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n updateLayout();\n\n let frame: number | undefined;\n const scheduleUpdate = () => {\n if (frame) return;\n\n frame = window.requestAnimationFrame(() => {\n frame = undefined;\n updateLayout();\n });\n };\n\n window.addEventListener('resize', scheduleUpdate);\n window.addEventListener('scroll', scheduleUpdate, true);\n\n return () => {\n window.removeEventListener('resize', scheduleUpdate);\n window.removeEventListener('scroll', scheduleUpdate, true);\n if (frame) window.cancelAnimationFrame(frame);\n };\n }, [isOpen, updateLayout]);\n\n // While the tooltip is there, the content is described by it (read by a screen reader when the\n // content has the focus). `useMemo` keeps the element the same between the renders the tooltip\n // makes by itself, so the content is not rendered again for each of them.\n const trigger = useMemo(() => {\n if (!required || !isOpen || !isValidElement<{ 'aria-describedby'?: string }>(children)) {\n return children;\n }\n\n const describedBy = children.props['aria-describedby'];\n\n return cloneElement(children, {\n 'aria-describedby': describedBy ? `${describedBy} ${id}` : id,\n });\n }, [children, id, isOpen, required]);\n\n const isShown = required && isOpen && layout !== undefined;\n\n return (\n <div\n ref={containerRef}\n className={required ? `relative inline-flex ${containerClassName}` : 'contents'}\n onBlur={closeTooltip}\n onFocus={openTooltip}\n onMouseEnter={openTooltip}\n onMouseLeave={closeTooltip}\n >\n {/* Main Content */}\n {trigger}\n {/* Tooltip */}\n {isShown &&\n createPortal(\n <div\n ref={tooltipRef}\n id={id}\n role=\"tooltip\"\n className={`border-silver-jet-2 bg-platinum-jet fixed z-9999 flex flex-col items-center justify-center rounded-lg border px-3 py-2 text-center whitespace-nowrap backdrop-blur-md transition-all duration-400 ease-in-out after:absolute after:border-8 after:border-solid after:border-transparent after:content-[''] ${isVisible ? 'visible opacity-100' : 'invisible opacity-0'} ${getTooltipArrowCss(layout.placement)} ${className} `}\n style={\n {\n ...layout.position,\n '--tooltip-arrow-shift': `${String(layout.arrowShift)}px`,\n transform: getTooltipTransform(layout.placement, isVisible),\n } as CSSProperties\n }\n >\n <div className=\"text-primary text-xs font-medium\">{title}</div>\n {description && (\n <div className=\"text-tertiary text-[10px] font-light\">{description}</div>\n )}\n </div>,\n document.body,\n )}\n </div>\n );\n};\n"]}