@behindthemusictree/app-kit-ui 26.1.0 → 28.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-KPWV4LEJ.js → chunk-5PQWJRDZ.js} +1061 -541
- package/dist/chunk-5PQWJRDZ.js.map +1 -0
- package/dist/chunk-DKQQLLMY.js +246 -0
- package/dist/chunk-DKQQLLMY.js.map +1 -0
- package/dist/{chunk-ZLCRD7OK.js → chunk-XUC4UJDQ.js} +2 -2
- package/dist/genre-tree/index.cjs +1260 -635
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +35 -8
- package/dist/genre-tree/index.js +2 -2
- package/dist/genre-tree/styles.css +80 -0
- package/dist/index.cjs +1286 -661
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -4
- package/dist/popup/index.cjs +179 -73
- package/dist/popup/index.cjs.map +1 -1
- package/dist/popup/index.d.ts +22 -2
- package/dist/popup/index.js +5 -3
- package/package.json +7 -7
- package/dist/chunk-KPWV4LEJ.js.map +0 -1
- package/dist/chunk-VDXUW7PP.js +0 -141
- package/dist/chunk-VDXUW7PP.js.map +0 -1
- /package/dist/{chunk-ZLCRD7OK.js.map → chunk-XUC4UJDQ.js.map} +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// src/popup/PopupTitle.tsx
|
|
2
|
+
import { X } from "lucide-react";
|
|
3
|
+
import { Button } from "@behindthemusictree/ui";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
function PopupTitle({
|
|
6
|
+
title,
|
|
7
|
+
onClose,
|
|
8
|
+
isDismissable,
|
|
9
|
+
icon: Icon,
|
|
10
|
+
className = "",
|
|
11
|
+
titleClassName = "",
|
|
12
|
+
closeButtonClassName = ""
|
|
13
|
+
}) {
|
|
14
|
+
return /* @__PURE__ */ jsxs("div", { className: `popup-title flex items-center justify-between bg-black p-4 rounded-t-lg ${className}`, children: [
|
|
15
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
16
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-white" }),
|
|
17
|
+
/* @__PURE__ */ jsx("h3", { id: "popup-title", className: `text-white font-semibold ${titleClassName}`, children: title })
|
|
18
|
+
] }),
|
|
19
|
+
isDismissable && /* @__PURE__ */ jsx(
|
|
20
|
+
Button,
|
|
21
|
+
{
|
|
22
|
+
onClick: onClose,
|
|
23
|
+
className: `h-8 w-8 text-white hover:text-white/80 ${closeButtonClassName}`,
|
|
24
|
+
"aria-label": "Close popup",
|
|
25
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
] });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/popup/PopupButtons.tsx
|
|
32
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
33
|
+
function PopupButtons({ children, alignment = "center", className = "" }) {
|
|
34
|
+
const alignmentClasses = {
|
|
35
|
+
left: "justify-start",
|
|
36
|
+
center: "justify-center",
|
|
37
|
+
right: "justify-end"
|
|
38
|
+
};
|
|
39
|
+
return /* @__PURE__ */ jsx2("div", { className: `flex gap-3 ${alignmentClasses[alignment]} ${className}`, children });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/popup/BasePopup.tsx
|
|
43
|
+
import { createPortal } from "react-dom";
|
|
44
|
+
import { useEffect, useRef } from "react";
|
|
45
|
+
import { Button as Button2 } from "@behindthemusictree/ui";
|
|
46
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
47
|
+
var typeClasses = {
|
|
48
|
+
default: "w-full max-w-lg",
|
|
49
|
+
success: "w-full max-w-lg",
|
|
50
|
+
error: "w-full max-w-lg",
|
|
51
|
+
warning: "w-full max-w-lg",
|
|
52
|
+
info: "w-full max-w-lg",
|
|
53
|
+
spotify: "w-full max-w-md",
|
|
54
|
+
auth: "w-full max-w-md"
|
|
55
|
+
};
|
|
56
|
+
var contentBgClasses = {
|
|
57
|
+
spotify: "bg-green-500",
|
|
58
|
+
auth: "bg-gray-800"
|
|
59
|
+
};
|
|
60
|
+
function BasePopup(props) {
|
|
61
|
+
const {
|
|
62
|
+
title,
|
|
63
|
+
children,
|
|
64
|
+
onClose,
|
|
65
|
+
type = "default",
|
|
66
|
+
icon,
|
|
67
|
+
isDismissable = true,
|
|
68
|
+
className,
|
|
69
|
+
overlayClassName = "",
|
|
70
|
+
titleClassName = "",
|
|
71
|
+
titleTextClassName = "",
|
|
72
|
+
closeButtonClassName = "",
|
|
73
|
+
fitContent = false,
|
|
74
|
+
contentClassName = "",
|
|
75
|
+
showOkButton = false,
|
|
76
|
+
showCancelButton = false,
|
|
77
|
+
okButtonText = "OK",
|
|
78
|
+
cancelButtonText = "Cancel",
|
|
79
|
+
onOk,
|
|
80
|
+
okButtonDisabled = false,
|
|
81
|
+
onCancel,
|
|
82
|
+
buttonAlignment = "center",
|
|
83
|
+
topOffset = 0,
|
|
84
|
+
trapFocus = false,
|
|
85
|
+
closeOnEscape = false,
|
|
86
|
+
closeOnBackdrop = false
|
|
87
|
+
} = props;
|
|
88
|
+
const dialogRef = useRef(null);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
91
|
+
const dialog = dialogRef.current;
|
|
92
|
+
const focusableSelector = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
93
|
+
const getFocusable = () => Array.from(dialog?.querySelectorAll(focusableSelector) ?? []).filter(
|
|
94
|
+
(element) => !element.hasAttribute("hidden") && element.getAttribute("aria-hidden") !== "true"
|
|
95
|
+
);
|
|
96
|
+
if (trapFocus) (getFocusable()[0] ?? dialog)?.focus();
|
|
97
|
+
const handleKeyDown = (event) => {
|
|
98
|
+
if (event.key === "Escape" && closeOnEscape && isDismissable) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
onClose?.();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!trapFocus || event.key !== "Tab") return;
|
|
104
|
+
const focusable = getFocusable();
|
|
105
|
+
if (focusable.length === 0) {
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
dialog?.focus();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const first = focusable[0];
|
|
111
|
+
const last = focusable[focusable.length - 1];
|
|
112
|
+
if (event.shiftKey && (document.activeElement === first || !dialog?.contains(document.activeElement))) {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
last.focus();
|
|
115
|
+
} else if (!event.shiftKey && (document.activeElement === last || !dialog?.contains(document.activeElement))) {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
first.focus();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
if (trapFocus || closeOnEscape) document.addEventListener("keydown", handleKeyDown);
|
|
121
|
+
return () => {
|
|
122
|
+
if (trapFocus || closeOnEscape) document.removeEventListener("keydown", handleKeyDown);
|
|
123
|
+
if (trapFocus && previousFocus?.isConnected) previousFocus.focus();
|
|
124
|
+
};
|
|
125
|
+
}, [closeOnEscape, isDismissable, onClose, trapFocus]);
|
|
126
|
+
const baseClasses = "bg-white rounded-lg";
|
|
127
|
+
const renderButtons = () => {
|
|
128
|
+
if (!showOkButton && !showCancelButton) return null;
|
|
129
|
+
return /* @__PURE__ */ jsx3("div", { className: "mt-4 pt-4 border-t", children: /* @__PURE__ */ jsxs2(PopupButtons, { alignment: buttonAlignment, children: [
|
|
130
|
+
showCancelButton && /* @__PURE__ */ jsx3(Button2, { onClick: onCancel || onClose || (() => {
|
|
131
|
+
}), variant: "secondary", children: cancelButtonText }),
|
|
132
|
+
showOkButton && /* @__PURE__ */ jsx3(Button2, { onClick: onOk || onClose || (() => {
|
|
133
|
+
}), disabled: okButtonDisabled, children: okButtonText })
|
|
134
|
+
] }) });
|
|
135
|
+
};
|
|
136
|
+
const popupComponent = /* @__PURE__ */ jsx3(
|
|
137
|
+
"div",
|
|
138
|
+
{
|
|
139
|
+
className: `fixed z-50 flex items-center justify-center bg-transparent ${overlayClassName}`,
|
|
140
|
+
style: {
|
|
141
|
+
top: topOffset,
|
|
142
|
+
left: 0,
|
|
143
|
+
right: 0,
|
|
144
|
+
bottom: 0
|
|
145
|
+
},
|
|
146
|
+
role: "dialog",
|
|
147
|
+
"aria-modal": "true",
|
|
148
|
+
"aria-labelledby": "popup-title",
|
|
149
|
+
onClick: closeOnBackdrop && isDismissable ? onClose : void 0,
|
|
150
|
+
children: /* @__PURE__ */ jsxs2(
|
|
151
|
+
"div",
|
|
152
|
+
{
|
|
153
|
+
ref: dialogRef,
|
|
154
|
+
tabIndex: trapFocus ? -1 : void 0,
|
|
155
|
+
className: `${baseClasses} ${fitContent ? typeClasses[type].replace("w-full", "w-fit") : typeClasses[type]} ${className || ""}`,
|
|
156
|
+
onClick: (e) => e.stopPropagation(),
|
|
157
|
+
children: [
|
|
158
|
+
/* @__PURE__ */ jsx3(
|
|
159
|
+
PopupTitle,
|
|
160
|
+
{
|
|
161
|
+
title,
|
|
162
|
+
onClose: onClose ?? (() => {
|
|
163
|
+
}),
|
|
164
|
+
isDismissable,
|
|
165
|
+
icon,
|
|
166
|
+
className: titleClassName,
|
|
167
|
+
titleClassName: titleTextClassName,
|
|
168
|
+
closeButtonClassName
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
/* @__PURE__ */ jsxs2("div", { className: `popup-content rounded-b-lg p-4 ${contentBgClasses[type] ?? ""} ${contentClassName}`, children: [
|
|
172
|
+
children,
|
|
173
|
+
renderButtons()
|
|
174
|
+
] })
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
return createPortal(popupComponent, document.body);
|
|
181
|
+
}
|
|
182
|
+
var BasePopup_default = BasePopup;
|
|
183
|
+
|
|
184
|
+
// src/popup/Toast.tsx
|
|
185
|
+
import { useEffect as useEffect2, useState } from "react";
|
|
186
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
187
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
188
|
+
function Toast({ message, onClose, durationMs = 2200 }) {
|
|
189
|
+
const [mounted, setMounted] = useState(false);
|
|
190
|
+
useEffect2(() => {
|
|
191
|
+
setMounted(true);
|
|
192
|
+
const timeout = window.setTimeout(onClose, durationMs);
|
|
193
|
+
return () => window.clearTimeout(timeout);
|
|
194
|
+
}, [durationMs, onClose]);
|
|
195
|
+
if (!mounted) return null;
|
|
196
|
+
return createPortal2(
|
|
197
|
+
/* @__PURE__ */ jsx4("div", { className: "pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4", children: /* @__PURE__ */ jsx4(
|
|
198
|
+
"div",
|
|
199
|
+
{
|
|
200
|
+
role: "status",
|
|
201
|
+
"aria-live": "polite",
|
|
202
|
+
"aria-atomic": "true",
|
|
203
|
+
className: "pointer-events-auto w-full max-w-md rounded-md bg-[var(--color-surface)] px-4 py-4 text-sm text-[var(--color-text)] shadow-lg ring-1 ring-black/10",
|
|
204
|
+
children: message
|
|
205
|
+
}
|
|
206
|
+
) }),
|
|
207
|
+
document.body
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/popup/InternalErrorPopup.tsx
|
|
212
|
+
import { AlertTriangle, AlertCircle } from "lucide-react";
|
|
213
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
214
|
+
function InternalErrorPopup({ errorCode, contactEmail, ...rest }) {
|
|
215
|
+
return /* @__PURE__ */ jsx5(
|
|
216
|
+
BasePopup,
|
|
217
|
+
{
|
|
218
|
+
...rest,
|
|
219
|
+
title: "Internal Error",
|
|
220
|
+
isDismissable: false,
|
|
221
|
+
icon: AlertTriangle,
|
|
222
|
+
children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center space-y-6 py-4", children: [
|
|
223
|
+
/* @__PURE__ */ jsx5(AlertCircle, { className: "h-16 w-16 text-[var(--color-red-500)]", strokeWidth: 1.5 }),
|
|
224
|
+
/* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsxs3("p", { className: "text-center text-[var(--color-neutral-600)]", children: [
|
|
225
|
+
"Please try again. If the problem persists, contact us at",
|
|
226
|
+
" ",
|
|
227
|
+
/* @__PURE__ */ jsx5("a", { href: `mailto:${contactEmail}`, className: "text-[var(--color-primary)] hover:text-[var(--color-primary-hover)]", children: contactEmail })
|
|
228
|
+
] }) }),
|
|
229
|
+
errorCode && /* @__PURE__ */ jsxs3("div", { className: "text-sm text-[var(--color-neutral-500)] bg-[var(--color-neutral-100)] p-3 rounded-lg border border-[var(--color-neutral-100)]", children: [
|
|
230
|
+
"Error Code: ",
|
|
231
|
+
errorCode
|
|
232
|
+
] })
|
|
233
|
+
] })
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export {
|
|
239
|
+
PopupTitle,
|
|
240
|
+
PopupButtons,
|
|
241
|
+
BasePopup,
|
|
242
|
+
BasePopup_default,
|
|
243
|
+
Toast,
|
|
244
|
+
InternalErrorPopup
|
|
245
|
+
};
|
|
246
|
+
//# sourceMappingURL=chunk-DKQQLLMY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/popup/PopupTitle.tsx","../src/popup/PopupButtons.tsx","../src/popup/BasePopup.tsx","../src/popup/Toast.tsx","../src/popup/InternalErrorPopup.tsx"],"sourcesContent":["\"use client\";\n\nimport { X } from \"lucide-react\";\nimport { Button } from \"@behindthemusictree/ui\";\nimport { LucideIcon } from \"lucide-react\";\n\ninterface PopupTitleProps {\n title: string;\n onClose: () => void;\n isDismissable?: boolean;\n icon?: LucideIcon;\n className?: string;\n titleClassName?: string;\n closeButtonClassName?: string;\n}\n\nexport function PopupTitle({\n title,\n onClose,\n isDismissable,\n icon: Icon,\n className = \"\",\n titleClassName = \"\",\n closeButtonClassName = \"\",\n}: PopupTitleProps) {\n return (\n <div className={`popup-title flex items-center justify-between bg-black p-4 rounded-t-lg ${className}`}>\n <div className=\"flex items-center gap-2\">\n {Icon && <Icon className=\"h-5 w-5 text-white\" />}\n <h3 id=\"popup-title\" className={`text-white font-semibold ${titleClassName}`}>\n {title}\n </h3>\n </div>\n {isDismissable && (\n <Button\n onClick={onClose}\n className={`h-8 w-8 text-white hover:text-white/80 ${closeButtonClassName}`}\n aria-label=\"Close popup\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { ReactNode } from \"react\";\n\ninterface PopupButtonsProps {\n children: ReactNode;\n alignment?: \"left\" | \"center\" | \"right\";\n className?: string;\n}\n\nexport function PopupButtons({ children, alignment = \"center\", className = \"\" }: PopupButtonsProps) {\n const alignmentClasses = {\n left: \"justify-start\",\n center: \"justify-center\",\n right: \"justify-end\",\n };\n\n return <div className={`flex gap-3 ${alignmentClasses[alignment]} ${className}`}>{children}</div>;\n}\n","\"use client\";\n\nimport { createPortal } from \"react-dom\";\nimport { ReactNode, useEffect, useRef } from \"react\";\nimport { PopupTitle } from \"./PopupTitle\";\nimport { PopupButtons } from \"./PopupButtons\";\nimport { Button } from \"@behindthemusictree/ui\";\nimport { LucideIcon } from \"lucide-react\";\n\nexport interface BasePopupProps {\n title: string;\n children: ReactNode;\n onClose?: () => void;\n type?: \"default\" | \"success\" | \"error\" | \"warning\" | \"info\" | \"spotify\" | \"auth\";\n icon?: LucideIcon;\n isDismissable?: boolean;\n className?: string;\n overlayClassName?: string;\n titleClassName?: string;\n titleTextClassName?: string;\n closeButtonClassName?: string;\n /** Sizes the popup to its content (up to the type's max width) instead of filling it. */\n fitContent?: boolean;\n contentClassName?: string;\n showOkButton?: boolean;\n showCancelButton?: boolean;\n okButtonText?: string;\n okButtonDisabled?: boolean;\n cancelButtonText?: string;\n onOk?: () => void;\n onCancel?: () => void;\n buttonAlignment?: \"left\" | \"center\" | \"right\";\n /**\n * Distance (in px) reserved at the top of the viewport, e.g. for a fixed app header/banner.\n * Each consuming app passes its own value. Defaults to 0.\n */\n topOffset?: number;\n /** Keeps keyboard focus within the popup and restores it to the opener when dismissed. */\n trapFocus?: boolean;\n /** Closes a dismissable popup on Escape. */\n closeOnEscape?: boolean;\n /** Closes a dismissable popup when the backdrop is clicked. */\n closeOnBackdrop?: boolean;\n}\n\nconst typeClasses: Record<string, string> = {\n default: \"w-full max-w-lg\",\n success: \"w-full max-w-lg\",\n error: \"w-full max-w-lg\",\n warning: \"w-full max-w-lg\",\n info: \"w-full max-w-lg\",\n spotify: \"w-full max-w-md\",\n auth: \"w-full max-w-md\",\n};\n\nconst contentBgClasses: Record<string, string> = {\n spotify: \"bg-green-500\",\n auth: \"bg-gray-800\",\n};\n\nexport function BasePopup(props: BasePopupProps) {\n const {\n title,\n children,\n onClose,\n type = \"default\",\n icon,\n isDismissable = true,\n className,\n overlayClassName = \"\",\n titleClassName = \"\",\n titleTextClassName = \"\",\n closeButtonClassName = \"\",\n fitContent = false,\n contentClassName = \"\",\n showOkButton = false,\n showCancelButton = false,\n okButtonText = \"OK\",\n cancelButtonText = \"Cancel\",\n onOk,\n okButtonDisabled = false,\n onCancel,\n buttonAlignment = \"center\",\n topOffset = 0,\n trapFocus = false,\n closeOnEscape = false,\n closeOnBackdrop = false,\n } = props;\n const dialogRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const dialog = dialogRef.current;\n const focusableSelector =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n const getFocusable = () =>\n Array.from(dialog?.querySelectorAll<HTMLElement>(focusableSelector) ?? []).filter(\n (element) => !element.hasAttribute(\"hidden\") && element.getAttribute(\"aria-hidden\") !== \"true\",\n );\n\n if (trapFocus) (getFocusable()[0] ?? dialog)?.focus();\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && closeOnEscape && isDismissable) {\n event.preventDefault();\n onClose?.();\n return;\n }\n if (!trapFocus || event.key !== \"Tab\") return;\n\n const focusable = getFocusable();\n if (focusable.length === 0) {\n event.preventDefault();\n dialog?.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (event.shiftKey && (document.activeElement === first || !dialog?.contains(document.activeElement))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (document.activeElement === last || !dialog?.contains(document.activeElement))) {\n event.preventDefault();\n first.focus();\n }\n };\n\n if (trapFocus || closeOnEscape) document.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n if (trapFocus || closeOnEscape) document.removeEventListener(\"keydown\", handleKeyDown);\n if (trapFocus && previousFocus?.isConnected) previousFocus.focus();\n };\n }, [closeOnEscape, isDismissable, onClose, trapFocus]);\n\n const baseClasses = \"bg-white rounded-lg\";\n\n const renderButtons = () => {\n if (!showOkButton && !showCancelButton) return null;\n\n return (\n <div className=\"mt-4 pt-4 border-t\">\n <PopupButtons alignment={buttonAlignment}>\n {showCancelButton && (\n <Button onClick={onCancel || onClose || (() => {})} variant=\"secondary\">\n {cancelButtonText}\n </Button>\n )}\n {showOkButton && (\n <Button onClick={onOk || onClose || (() => {})} disabled={okButtonDisabled}>\n {okButtonText}\n </Button>\n )}\n </PopupButtons>\n </div>\n );\n };\n\n const popupComponent = (\n <div\n className={`fixed z-50 flex items-center justify-center bg-transparent ${overlayClassName}`}\n style={{\n top: topOffset,\n left: 0,\n right: 0,\n bottom: 0,\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"popup-title\"\n onClick={closeOnBackdrop && isDismissable ? onClose : undefined}\n >\n <div\n ref={dialogRef}\n tabIndex={trapFocus ? -1 : undefined}\n className={`${baseClasses} ${fitContent ? typeClasses[type].replace(\"w-full\", \"w-fit\") : typeClasses[type]} ${className || \"\"}`}\n onClick={(e) => e.stopPropagation()}\n >\n <PopupTitle\n title={title}\n onClose={onClose ?? (() => {})}\n isDismissable={isDismissable}\n icon={icon}\n className={titleClassName}\n titleClassName={titleTextClassName}\n closeButtonClassName={closeButtonClassName}\n />\n <div className={`popup-content rounded-b-lg p-4 ${contentBgClasses[type] ?? \"\"} ${contentClassName}`}>\n {children}\n {renderButtons()}\n </div>\n </div>\n </div>\n );\n\n return createPortal(popupComponent, document.body);\n}\n\nexport default BasePopup;\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nexport type ToastProps = {\n message: string;\n onClose: () => void;\n durationMs?: number;\n};\n\nexport default function Toast({ message, onClose, durationMs = 2200 }: ToastProps) {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n const timeout = window.setTimeout(onClose, durationMs);\n return () => window.clearTimeout(timeout);\n }, [durationMs, onClose]);\n\n if (!mounted) return null;\n\n return createPortal(\n <div className=\"pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4\">\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className=\"pointer-events-auto w-full max-w-md rounded-md bg-[var(--color-surface)] px-4 py-4 text-sm text-[var(--color-text)] shadow-lg ring-1 ring-black/10\"\n >\n {message}\n </div>\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { AlertTriangle, AlertCircle } from \"lucide-react\";\nimport { BasePopup, BasePopupProps } from \"./BasePopup\";\nimport { ErrorCode } from \"@behindthemusictree/app-kit/transport\";\n\ntype InternalErrorPopupProps = Omit<BasePopupProps, \"title\" | \"children\" | \"icon\" | \"isDismissable\"> & {\n errorCode: ErrorCode;\n /**\n * Support contact email shown in the \"if the problem persists\" message. Passed by the\n * consuming app (from whatever env var / config it uses) so this package doesn't assume a\n * specific env var name.\n */\n contactEmail?: string | null;\n};\n\nexport default function InternalErrorPopup({ errorCode, contactEmail, ...rest }: InternalErrorPopupProps) {\n return (\n <BasePopup\n {...rest}\n title=\"Internal Error\"\n isDismissable={false}\n icon={AlertTriangle}\n children={\n <div className=\"flex flex-col items-center space-y-6 py-4\">\n <AlertCircle className=\"h-16 w-16 text-[var(--color-red-500)]\" strokeWidth={1.5} />\n <div>\n <p className=\"text-center text-[var(--color-neutral-600)]\">\n Please try again. If the problem persists, contact us at{\" \"}\n <a href={`mailto:${contactEmail}`} className=\"text-[var(--color-primary)] hover:text-[var(--color-primary-hover)]\">\n {contactEmail}\n </a>\n </p>\n </div>\n {errorCode && (\n <div className=\"text-sm text-[var(--color-neutral-500)] bg-[var(--color-neutral-100)] p-3 rounded-lg border border-[var(--color-neutral-100)]\">\n Error Code: {errorCode}\n </div>\n )}\n </div>\n }\n />\n );\n}\n"],"mappings":";AAEA,SAAS,SAAS;AAClB,SAAS,cAAc;AAwBjB,SACW,KADX;AAXC,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,uBAAuB;AACzB,GAAoB;AAClB,SACE,qBAAC,SAAI,WAAW,2EAA2E,SAAS,IAClG;AAAA,yBAAC,SAAI,WAAU,2BACZ;AAAA,cAAQ,oBAAC,QAAK,WAAU,sBAAqB;AAAA,MAC9C,oBAAC,QAAG,IAAG,eAAc,WAAW,4BAA4B,cAAc,IACvE,iBACH;AAAA,OACF;AAAA,IACC,iBACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAW,0CAA0C,oBAAoB;AAAA,QACzE,cAAW;AAAA,QAEX,8BAAC,KAAE,WAAU,WAAU;AAAA;AAAA,IACzB;AAAA,KAEJ;AAEJ;;;AC3BS,gBAAAA,YAAA;AAPF,SAAS,aAAa,EAAE,UAAU,YAAY,UAAU,YAAY,GAAG,GAAsB;AAClG,QAAM,mBAAmB;AAAA,IACvB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,EACT;AAEA,SAAO,gBAAAA,KAAC,SAAI,WAAW,cAAc,iBAAiB,SAAS,CAAC,IAAI,SAAS,IAAK,UAAS;AAC7F;;;AChBA,SAAS,oBAAoB;AAC7B,SAAoB,WAAW,cAAc;AAG7C,SAAS,UAAAC,eAAc;AAwIf,SAEI,OAAAC,MAFJ,QAAAC,aAAA;AAjGR,IAAM,cAAsC;AAAA,EAC1C,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AACR;AAEA,IAAM,mBAA2C;AAAA,EAC/C,SAAS;AAAA,EACT,MAAM;AACR;AAEO,SAAS,UAAU,OAAuB;AAC/C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,IACA,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,uBAAuB;AAAA,IACvB,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,mBAAmB;AAAA,IACnB;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA,kBAAkB;AAAA,IAClB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB,IAAI;AACJ,QAAM,YAAY,OAAuB,IAAI;AAE7C,YAAU,MAAM;AACd,UAAM,gBAAgB,SAAS,yBAAyB,cAAc,SAAS,gBAAgB;AAC/F,UAAM,SAAS,UAAU;AACzB,UAAM,oBACJ;AACF,UAAM,eAAe,MACnB,MAAM,KAAK,QAAQ,iBAA8B,iBAAiB,KAAK,CAAC,CAAC,EAAE;AAAA,MACzE,CAAC,YAAY,CAAC,QAAQ,aAAa,QAAQ,KAAK,QAAQ,aAAa,aAAa,MAAM;AAAA,IAC1F;AAEF,QAAI,UAAW,EAAC,aAAa,EAAE,CAAC,KAAK,SAAS,MAAM;AAEpD,UAAM,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,QAAQ,YAAY,iBAAiB,eAAe;AAC5D,cAAM,eAAe;AACrB,kBAAU;AACV;AAAA,MACF;AACA,UAAI,CAAC,aAAa,MAAM,QAAQ,MAAO;AAEvC,YAAM,YAAY,aAAa;AAC/B,UAAI,UAAU,WAAW,GAAG;AAC1B,cAAM,eAAe;AACrB,gBAAQ,MAAM;AACd;AAAA,MACF;AAEA,YAAM,QAAQ,UAAU,CAAC;AACzB,YAAM,OAAO,UAAU,UAAU,SAAS,CAAC;AAC3C,UAAI,MAAM,aAAa,SAAS,kBAAkB,SAAS,CAAC,QAAQ,SAAS,SAAS,aAAa,IAAI;AACrG,cAAM,eAAe;AACrB,aAAK,MAAM;AAAA,MACb,WAAW,CAAC,MAAM,aAAa,SAAS,kBAAkB,QAAQ,CAAC,QAAQ,SAAS,SAAS,aAAa,IAAI;AAC5G,cAAM,eAAe;AACrB,cAAM,MAAM;AAAA,MACd;AAAA,IACF;AAEA,QAAI,aAAa,cAAe,UAAS,iBAAiB,WAAW,aAAa;AAClF,WAAO,MAAM;AACX,UAAI,aAAa,cAAe,UAAS,oBAAoB,WAAW,aAAa;AACrF,UAAI,aAAa,eAAe,YAAa,eAAc,MAAM;AAAA,IACnE;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,SAAS,SAAS,CAAC;AAErD,QAAM,cAAc;AAEpB,QAAM,gBAAgB,MAAM;AAC1B,QAAI,CAAC,gBAAgB,CAAC,iBAAkB,QAAO;AAE/C,WACE,gBAAAD,KAAC,SAAI,WAAU,sBACb,0BAAAC,MAAC,gBAAa,WAAW,iBACtB;AAAA,0BACC,gBAAAD,KAACD,SAAA,EAAO,SAAS,YAAY,YAAY,MAAM;AAAA,MAAC,IAAI,SAAQ,aACzD,4BACH;AAAA,MAED,gBACC,gBAAAC,KAACD,SAAA,EAAO,SAAS,QAAQ,YAAY,MAAM;AAAA,MAAC,IAAI,UAAU,kBACvD,wBACH;AAAA,OAEJ,GACF;AAAA,EAEJ;AAEA,QAAM,iBACJ,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,8DAA8D,gBAAgB;AAAA,MACzF,OAAO;AAAA,QACL,KAAK;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,MACA,MAAK;AAAA,MACL,cAAW;AAAA,MACX,mBAAgB;AAAA,MAChB,SAAS,mBAAmB,gBAAgB,UAAU;AAAA,MAEtD,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,UAAU,YAAY,KAAK;AAAA,UAC3B,WAAW,GAAG,WAAW,IAAI,aAAa,YAAY,IAAI,EAAE,QAAQ,UAAU,OAAO,IAAI,YAAY,IAAI,CAAC,IAAI,aAAa,EAAE;AAAA,UAC7H,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,UAElC;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA,SAAS,YAAY,MAAM;AAAA,gBAAC;AAAA,gBAC5B;AAAA,gBACA;AAAA,gBACA,WAAW;AAAA,gBACX,gBAAgB;AAAA,gBAChB;AAAA;AAAA,YACF;AAAA,YACA,gBAAAC,MAAC,SAAI,WAAW,kCAAkC,iBAAiB,IAAI,KAAK,EAAE,IAAI,gBAAgB,IAC/F;AAAA;AAAA,cACA,cAAc;AAAA,eACjB;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAGF,SAAO,aAAa,gBAAgB,SAAS,IAAI;AACnD;AAEA,IAAO,oBAAQ;;;ACpMf,SAAS,aAAAC,YAAW,gBAAgB;AACpC,SAAS,gBAAAC,qBAAoB;AAqBvB,gBAAAC,YAAA;AAbS,SAAR,MAAuB,EAAE,SAAS,SAAS,aAAa,KAAK,GAAe;AACjF,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,EAAAF,WAAU,MAAM;AACd,eAAW,IAAI;AACf,UAAM,UAAU,OAAO,WAAW,SAAS,UAAU;AACrD,WAAO,MAAM,OAAO,aAAa,OAAO;AAAA,EAC1C,GAAG,CAAC,YAAY,OAAO,CAAC;AAExB,MAAI,CAAC,QAAS,QAAO;AAErB,SAAOC;AAAA,IACL,gBAAAC,KAAC,SAAI,WAAU,gFACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,eAAY;AAAA,QACZ,WAAU;AAAA,QAET;AAAA;AAAA,IACH,GACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;ACjCA,SAAS,eAAe,mBAAmB;AAuBjC,gBAAAC,MAEE,QAAAC,aAFF;AATK,SAAR,mBAAoC,EAAE,WAAW,cAAc,GAAG,KAAK,GAA4B;AACxG,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,OAAM;AAAA,MACN,eAAe;AAAA,MACf,MAAM;AAAA,MACN,UACE,gBAAAC,MAAC,SAAI,WAAU,6CACb;AAAA,wBAAAD,KAAC,eAAY,WAAU,yCAAwC,aAAa,KAAK;AAAA,QACjF,gBAAAA,KAAC,SACC,0BAAAC,MAAC,OAAE,WAAU,+CAA8C;AAAA;AAAA,UACA;AAAA,UACzD,gBAAAD,KAAC,OAAE,MAAM,UAAU,YAAY,IAAI,WAAU,uEAC1C,wBACH;AAAA,WACF,GACF;AAAA,QACC,aACC,gBAAAC,MAAC,SAAI,WAAU,iIAAgI;AAAA;AAAA,UAChI;AAAA,WACf;AAAA,SAEJ;AAAA;AAAA,EAEJ;AAEJ;","names":["jsx","Button","jsx","jsxs","useEffect","createPortal","jsx","jsx","jsxs"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BasePopup
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DKQQLLMY.js";
|
|
4
4
|
|
|
5
5
|
// src/popup/TrackUploadPopup.tsx
|
|
6
6
|
import { MdError, MdCheckCircle, MdUpload } from "react-icons/md";
|
|
@@ -271,4 +271,4 @@ export {
|
|
|
271
271
|
AuthPopup,
|
|
272
272
|
SpotifyAuthErrorPopup
|
|
273
273
|
};
|
|
274
|
-
//# sourceMappingURL=chunk-
|
|
274
|
+
//# sourceMappingURL=chunk-XUC4UJDQ.js.map
|