jaml-ui 0.8.0 → 0.10.0
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/LICENSE +21 -21
- package/README.md +135 -135
- package/dist/assets.js +1 -1
- package/dist/components/CardFan.d.ts +22 -0
- package/dist/components/CardFan.js +80 -0
- package/dist/components/DeckSprite.d.ts +26 -0
- package/dist/components/DeckSprite.js +73 -0
- package/dist/components/JamlCodeEditor.d.ts +7 -0
- package/dist/components/JamlCodeEditor.js +58 -0
- package/dist/components/JamlIde.js +2 -14
- package/dist/components/JamlIdeVisual.js +4 -4
- package/dist/components/MotelyVersionBadge.d.ts +30 -0
- package/dist/components/MotelyVersionBadge.js +31 -0
- package/dist/hooks/searchWorkerCode.js +59 -59
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/ui/footer.js +5 -5
- package/dist/ui/jimboBackground.d.ts +14 -0
- package/dist/ui/jimboBackground.js +155 -0
- package/dist/ui/jimboFilterBar.d.ts +25 -0
- package/dist/ui/jimboFilterBar.js +77 -0
- package/dist/ui/jimboFlankNav.d.ts +18 -0
- package/dist/ui/jimboFlankNav.js +43 -0
- package/dist/ui/jimboTabs.d.ts +25 -0
- package/dist/ui/jimboTabs.js +59 -0
- package/dist/ui/jimboText.d.ts +26 -0
- package/dist/ui/jimboText.js +45 -0
- package/dist/ui/jimboTooltip.d.ts +35 -0
- package/dist/ui/jimboTooltip.js +109 -0
- package/dist/ui/panel.js +4 -6
- package/dist/ui/tokens.d.ts +2 -1
- package/dist/ui/tokens.js +2 -0
- package/dist/ui.d.ts +6 -0
- package/dist/ui.js +6 -0
- package/fonts.css +5 -0
- package/package.json +14 -3
- package/dist/data/balatro-jokers.json +0 -1241
- package/dist/r3f/BalatroJokerMesh3D.d.ts +0 -8
- package/dist/r3f/BalatroJokerMesh3D.js +0 -98
- package/dist/r3f/BalatroJokerPreview3D.d.ts +0 -14
- package/dist/r3f/BalatroJokerPreview3D.js +0 -30
- package/dist/r3f/BalatroPlayingCard3D.d.ts +0 -22
- package/dist/r3f/BalatroPlayingCard3D.js +0 -62
- package/dist/r3f/cardConstants.d.ts +0 -16
- package/dist/r3f/cardConstants.js +0 -14
- package/dist/r3f/compositedAtlas.d.ts +0 -5
- package/dist/r3f/compositedAtlas.js +0 -56
- package/dist/r3f/gridUV.d.ts +0 -22
- package/dist/r3f/gridUV.js +0 -30
- package/dist/r3f/index.d.ts +0 -12
- package/dist/r3f/index.js +0 -13
- package/dist/r3f/jokerRegistry.d.ts +0 -28
- package/dist/r3f/jokerRegistry.js +0 -40
- package/dist/r3f/jokerTilt.d.ts +0 -8
- package/dist/r3f/jokerTilt.js +0 -41
- package/dist/r3f/magneticTilt.d.ts +0 -18
- package/dist/r3f/magneticTilt.js +0 -34
- package/dist/r3f/playingCardTypes.d.ts +0 -24
- package/dist/r3f/playingCardTypes.js +0 -32
- package/dist/r3f/playingCardVisuals.d.ts +0 -7
- package/dist/r3f/playingCardVisuals.js +0 -45
- package/dist/r3f/usePlayingCardTexture.d.ts +0 -7
- package/dist/r3f/usePlayingCardTexture.js +0 -92
|
@@ -35,7 +35,7 @@ function DragClausePill({ clause, zone, onDragStart, }) {
|
|
|
35
35
|
borderRadius: 6, padding: "5px 8px 5px 4px",
|
|
36
36
|
boxShadow: `0 2px 0 ${JimboColorOption.BLACK}`,
|
|
37
37
|
cursor: "grab", userSelect: "none", touchAction: "none",
|
|
38
|
-
}, children: [_jsx("div", { style: { color: JimboColorOption.GREY, fontSize: 12, lineHeight: 1, padding: "0 2px" }, children: "\u22EE\u22EE" }), _jsx(ClauseSprite, { clause: clause, size: 26 }), _jsx("div", { style: { fontSize: 10, color: JimboColorOption.WHITE, letterSpacing: 1, textShadow:
|
|
38
|
+
}, children: [_jsx("div", { style: { color: JimboColorOption.GREY, fontSize: 12, lineHeight: 1, padding: "0 2px" }, children: "\u22EE\u22EE" }), _jsx(ClauseSprite, { clause: clause, size: 26 }), _jsx("div", { style: { fontSize: 10, color: JimboColorOption.WHITE, letterSpacing: 1, textShadow: `1px 1px 0 ${JimboColorOption.BLACK}cc` }, children: clause.label || clause.value }), clause.antes && clause.antes.length > 0 && (_jsxs("div", { style: { display: "flex", gap: 2 }, children: [clause.antes.slice(0, 3).map((a) => (_jsx("div", { style: { fontSize: 8, padding: "0 3px", background: JimboColorOption.DARKEST, color: z.color, borderRadius: 2 }, children: a }, a))), clause.antes.length > 3 && _jsxs("div", { style: { fontSize: 8, color: JimboColorOption.GREY }, children: ["+", clause.antes.length - 3] })] })), clause.score != null && (_jsxs("div", { style: { fontSize: 9, padding: "0 4px", background: JimboColorOption.RED, color: JimboColorOption.WHITE, borderRadius: 2 }, children: ["+", clause.score] }))] }));
|
|
39
39
|
}
|
|
40
40
|
function ZoneDropRail({ zone, clauses, onDragStart, highlight, }) {
|
|
41
41
|
const z = ZONE_META[zone];
|
|
@@ -47,7 +47,7 @@ function ZoneDropRail({ zone, clauses, onDragStart, highlight, }) {
|
|
|
47
47
|
}, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [_jsx("div", { style: {
|
|
48
48
|
fontSize: 10, letterSpacing: 2, padding: "2px 8px",
|
|
49
49
|
background: z.color, color: JimboColorOption.WHITE, borderRadius: 3,
|
|
50
|
-
textShadow:
|
|
50
|
+
textShadow: `1px 1px 0 ${JimboColorOption.BLACK}cc`,
|
|
51
51
|
}, children: z.label }), _jsx("div", { style: { flex: 1, height: 1, background: `${z.color}44` } }), _jsx("div", { style: { fontSize: 8, color: JimboColorOption.GREY }, children: clauses.length })] }), _jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: [clauses.map((c) => (_jsx(DragClausePill, { clause: c, zone: zone, onDragStart: onDragStart }, c.id))), clauses.length === 0 && (_jsx("div", { style: { fontSize: 10, color: JimboColorOption.GREY, padding: 10, fontStyle: "italic" }, children: "drop clauses here" }))] })] }));
|
|
52
52
|
}
|
|
53
53
|
export function JamlIdeVisual({ filter, onChange, onSave, onBack }) {
|
|
@@ -103,11 +103,11 @@ export function JamlIdeVisual({ filter, onChange, onSave, onBack }) {
|
|
|
103
103
|
return (_jsxs("div", { ref: rootRef, style: { display: "flex", flexDirection: "column", gap: 10, padding: 10 }, children: [_jsxs("div", { style: {
|
|
104
104
|
background: JimboColorOption.DARK_GREY, border: `2px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
105
105
|
borderRadius: 6, padding: 8, boxShadow: `0 2px 0 ${JimboColorOption.BLACK}`,
|
|
106
|
-
}, children: [_jsx("div", { style: { fontSize: 9, color: JimboColorOption.GREY, letterSpacing: 2 }, children: "FILE" }), _jsxs("div", { style: { fontSize: 14, color: JimboColorOption.WHITE, textShadow:
|
|
106
|
+
}, children: [_jsx("div", { style: { fontSize: 9, color: JimboColorOption.GREY, letterSpacing: 2 }, children: "FILE" }), _jsxs("div", { style: { fontSize: 14, color: JimboColorOption.WHITE, textShadow: `1px 1px 0 ${JimboColorOption.BLACK}cc` }, children: [filter.name || "Untitled", ".jaml"] }), filter.author && (_jsxs("div", { style: { fontSize: 9, color: JimboColorOption.GOLD_TEXT, marginTop: 2 }, children: ["by ", filter.author] }))] }), _jsx(ZoneDropRail, { zone: "must", clauses: filter.must, onDragStart: onDragStart, highlight: hoverZone === "must" }), _jsx(ZoneDropRail, { zone: "should", clauses: filter.should, onDragStart: onDragStart, highlight: hoverZone === "should" }), _jsx(ZoneDropRail, { zone: "mustnot", clauses: filter.mustnot, onDragStart: onDragStart, highlight: hoverZone === "mustnot" }), drag && (_jsx("div", { style: {
|
|
107
107
|
position: "fixed",
|
|
108
108
|
left: drag.x - drag.offX, top: drag.y - drag.offY,
|
|
109
109
|
pointerEvents: "none", zIndex: 999,
|
|
110
110
|
transform: "rotate(-2deg) scale(1.05)",
|
|
111
|
-
filter:
|
|
111
|
+
filter: `drop-shadow(0 4px 6px ${JimboColorOption.BLACK}99)`, opacity: 0.92,
|
|
112
112
|
}, children: _jsx(DragClausePill, { clause: drag.clause, zone: drag.fromZone, onDragStart: () => { } }) }))] }));
|
|
113
113
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface MotelyCapabilities {
|
|
3
|
+
version: string;
|
|
4
|
+
simd?: boolean;
|
|
5
|
+
threads?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface MotelyVersionBadgeProps {
|
|
8
|
+
/**
|
|
9
|
+
* Runtime capabilities from `motely.getCapabilities()`. If omitted, the
|
|
10
|
+
* consumer can pass a static `version` (typically from motely-wasm's
|
|
11
|
+
* own package.json) and the component will render without the SIMD /
|
|
12
|
+
* threads indicators.
|
|
13
|
+
*/
|
|
14
|
+
caps?: MotelyCapabilities | null;
|
|
15
|
+
/** Static fallback version when `caps` is null/undefined. */
|
|
16
|
+
version?: string;
|
|
17
|
+
/** Compact single-line badge instead of the labelled chip. Default false. */
|
|
18
|
+
minimal?: boolean;
|
|
19
|
+
/** Loading placeholder (shown while caps are being fetched). */
|
|
20
|
+
loading?: boolean;
|
|
21
|
+
className?: string;
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Badge showing the loaded motely-wasm version + optional SIMD / threads
|
|
26
|
+
* capability indicators. Ported from weejoker.app with no dependency on
|
|
27
|
+
* weejoker's lib/api — the consumer owns capability fetching and passes
|
|
28
|
+
* the result in.
|
|
29
|
+
*/
|
|
30
|
+
export declare function MotelyVersionBadge({ caps, version, minimal, loading, className, style, }: MotelyVersionBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { JimboColorOption } from '../ui/tokens.js';
|
|
4
|
+
import { JimboText } from '../ui/jimboText.js';
|
|
5
|
+
/**
|
|
6
|
+
* Badge showing the loaded motely-wasm version + optional SIMD / threads
|
|
7
|
+
* capability indicators. Ported from weejoker.app with no dependency on
|
|
8
|
+
* weejoker's lib/api — the consumer owns capability fetching and passes
|
|
9
|
+
* the result in.
|
|
10
|
+
*/
|
|
11
|
+
export function MotelyVersionBadge({ caps, version, minimal = false, loading = false, className = '', style, }) {
|
|
12
|
+
if (loading) {
|
|
13
|
+
return (_jsx("span", { className: className, style: style, children: _jsx(JimboText, { size: "xs", tone: "grey", children: "Initializing\u2026" }) }));
|
|
14
|
+
}
|
|
15
|
+
const resolved = caps?.version ?? version ?? '?';
|
|
16
|
+
const simd = caps?.simd;
|
|
17
|
+
const threads = caps?.threads;
|
|
18
|
+
if (minimal) {
|
|
19
|
+
return (_jsxs("span", { className: className, style: { display: 'inline-flex', alignItems: 'center', gap: 6, ...style }, children: [_jsxs(JimboText, { size: "xs", tone: "grey", children: ["v", resolved] }), simd ? (_jsx(JimboText, { size: "xs", tone: "blue", title: "SIMD enabled", children: "\u26A1" })) : null, threads ? (_jsx(JimboText, { size: "xs", tone: "green", title: "Multi-threaded", children: "\uD83E\uDDF5" })) : null] }));
|
|
20
|
+
}
|
|
21
|
+
return (_jsxs("div", { className: className, style: {
|
|
22
|
+
display: 'inline-flex',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
gap: 6,
|
|
25
|
+
padding: '3px 8px',
|
|
26
|
+
borderRadius: 4,
|
|
27
|
+
background: JimboColorOption.DARKEST,
|
|
28
|
+
border: `1px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
29
|
+
...style,
|
|
30
|
+
}, children: [_jsxs(JimboText, { size: "xs", tone: "gold", uppercase: true, children: ["motely v", resolved] }), simd ? _jsx(JimboText, { size: "xs", tone: "blue", title: "SIMD enabled", children: "\u26A1" }) : null, threads ? _jsx(JimboText, { size: "xs", tone: "green", title: "Multi-threaded", children: "\uD83E\uDDF5" }) : null] }));
|
|
31
|
+
}
|
|
@@ -1,62 +1,62 @@
|
|
|
1
1
|
// Worker code as an inline string — created as a Blob URL at runtime.
|
|
2
2
|
// This avoids bundler/import.meta.url issues when shipped as an npm package.
|
|
3
|
-
export const SEARCH_WORKER_CODE = `
|
|
4
|
-
let MotelyWasm = null;
|
|
5
|
-
let MotelyWasmEvents = null;
|
|
6
|
-
let activeSearch = null;
|
|
7
|
-
|
|
8
|
-
self.addEventListener('message', async function(e) {
|
|
9
|
-
const msg = e.data;
|
|
10
|
-
|
|
11
|
-
if (msg.type === 'init') {
|
|
12
|
-
try {
|
|
13
|
-
const mod = await import(msg.url);
|
|
14
|
-
await mod.default.boot();
|
|
15
|
-
MotelyWasm = mod.MotelyWasm;
|
|
16
|
-
MotelyWasmEvents = mod.MotelyWasmEvents;
|
|
17
|
-
self.postMessage({ type: 'ready' });
|
|
18
|
-
} catch (err) {
|
|
19
|
-
self.postMessage({ type: 'error', message: String(err) });
|
|
20
|
-
}
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (msg.type === 'start') {
|
|
25
|
-
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
26
|
-
const validation = MotelyWasm.validateJaml(msg.jaml);
|
|
27
|
-
if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
|
|
28
|
-
|
|
29
|
-
let rId, pId, cId;
|
|
30
|
-
function cleanup() {
|
|
31
|
-
MotelyWasmEvents.onResult.unsubscribeById(rId);
|
|
32
|
-
MotelyWasmEvents.onProgress.unsubscribeById(pId);
|
|
33
|
-
MotelyWasmEvents.onComplete.unsubscribeById(cId);
|
|
34
|
-
activeSearch = null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
rId = MotelyWasmEvents.onResult.subscribe(function(seed, score) {
|
|
38
|
-
self.postMessage({ type: 'result', seed, score });
|
|
39
|
-
});
|
|
40
|
-
pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {
|
|
41
|
-
self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
|
|
42
|
-
});
|
|
43
|
-
cId = MotelyWasmEvents.onComplete.subscribe(function(status, searched, matched) {
|
|
44
|
-
cleanup();
|
|
45
|
-
self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
50
|
-
} catch (err) {
|
|
51
|
-
cleanup();
|
|
52
|
-
self.postMessage({ type: 'error', message: String(err) });
|
|
53
|
-
}
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (msg.type === 'stop') {
|
|
58
|
-
if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
|
|
59
|
-
self.postMessage({ type: 'cancelled' });
|
|
60
|
-
}
|
|
61
|
-
});
|
|
3
|
+
export const SEARCH_WORKER_CODE = `
|
|
4
|
+
let MotelyWasm = null;
|
|
5
|
+
let MotelyWasmEvents = null;
|
|
6
|
+
let activeSearch = null;
|
|
7
|
+
|
|
8
|
+
self.addEventListener('message', async function(e) {
|
|
9
|
+
const msg = e.data;
|
|
10
|
+
|
|
11
|
+
if (msg.type === 'init') {
|
|
12
|
+
try {
|
|
13
|
+
const mod = await import(msg.url);
|
|
14
|
+
await mod.default.boot();
|
|
15
|
+
MotelyWasm = mod.MotelyWasm;
|
|
16
|
+
MotelyWasmEvents = mod.MotelyWasmEvents;
|
|
17
|
+
self.postMessage({ type: 'ready' });
|
|
18
|
+
} catch (err) {
|
|
19
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (msg.type === 'start') {
|
|
25
|
+
if (!MotelyWasm) { self.postMessage({ type: 'error', message: 'Not initialized' }); return; }
|
|
26
|
+
const validation = MotelyWasm.validateJaml(msg.jaml);
|
|
27
|
+
if (validation !== 'valid') { self.postMessage({ type: 'error', message: validation }); return; }
|
|
28
|
+
|
|
29
|
+
let rId, pId, cId;
|
|
30
|
+
function cleanup() {
|
|
31
|
+
MotelyWasmEvents.onResult.unsubscribeById(rId);
|
|
32
|
+
MotelyWasmEvents.onProgress.unsubscribeById(pId);
|
|
33
|
+
MotelyWasmEvents.onComplete.unsubscribeById(cId);
|
|
34
|
+
activeSearch = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
rId = MotelyWasmEvents.onResult.subscribe(function(seed, score) {
|
|
38
|
+
self.postMessage({ type: 'result', seed, score });
|
|
39
|
+
});
|
|
40
|
+
pId = MotelyWasmEvents.onProgress.subscribe(function(searched, matching) {
|
|
41
|
+
self.postMessage({ type: 'progress', searched: searched.toString(), matching: matching.toString() });
|
|
42
|
+
});
|
|
43
|
+
cId = MotelyWasmEvents.onComplete.subscribe(function(status, searched, matched) {
|
|
44
|
+
cleanup();
|
|
45
|
+
self.postMessage({ type: 'complete', status, searched: searched.toString(), matched: matched.toString() });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
activeSearch = MotelyWasm.startRandomSearch(msg.jaml, msg.count);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
cleanup();
|
|
52
|
+
self.postMessage({ type: 'error', message: String(err) });
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (msg.type === 'stop') {
|
|
58
|
+
if (activeSearch) { activeSearch.cancel(); activeSearch = null; }
|
|
59
|
+
self.postMessage({ type: 'cancelled' });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
62
|
`;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,18 @@ export { AnalyzerExplorer, type AnalyzerAnteView, type AnalyzerBadge, type Analy
|
|
|
6
6
|
export { JamlMapPreview, type JamlMapPreviewProps } from "./components/JamlMapPreview.js";
|
|
7
7
|
export { JamlIde, type JamlIdeProps, type JamlIdeSearchResult, type JamlVisualFilter, type JamlVisualClause, type JamlZone, } from "./components/JamlIde.js";
|
|
8
8
|
export { JamlIdeVisual, type JamlIdeVisualProps, } from "./components/JamlIdeVisual.js";
|
|
9
|
+
export { JamlCodeEditor, type JamlCodeEditorProps, } from "./components/JamlCodeEditor.js";
|
|
10
|
+
export { JimboText, type JimboTextProps, type JimboTextTone, type JimboTextSize, } from "./ui/jimboText.js";
|
|
11
|
+
export { JimboTabs, JimboVerticalTabs, type JimboTabItem, type JimboTabsProps, } from "./ui/jimboTabs.js";
|
|
12
|
+
export { JimboFlankNav, type JimboFlankNavProps, } from "./ui/jimboFlankNav.js";
|
|
13
|
+
export { JimboFilterBar, type JimboFilterBarProps, type JimboFilterSortOption, } from "./ui/jimboFilterBar.js";
|
|
14
|
+
export { JimboBackground } from "./ui/jimboBackground.js";
|
|
15
|
+
export { JimboTooltip, type JimboTooltipProps, type JimboTooltipMode, type JimboTooltipPlacement, } from "./ui/jimboTooltip.js";
|
|
9
16
|
export { JamlIdeToolbar, type JamlIdeMode, type JamlIdeToolbarProps, } from "./components/JamlIdeToolbar.js";
|
|
10
17
|
export { CardList, type CardListProps } from "./components/CardList.js";
|
|
18
|
+
export { CardFan, type CardFanProps } from "./components/CardFan.js";
|
|
19
|
+
export { DeckSprite, DECK_SPRITE_POS, STAKE_SPRITE_POS, type DeckSpriteProps, } from "./components/DeckSprite.js";
|
|
20
|
+
export { MotelyVersionBadge, type MotelyVersionBadgeProps, type MotelyCapabilities, } from "./components/MotelyVersionBadge.js";
|
|
11
21
|
export { extractVisualJamlItems, type JamlPreviewGroups, type JamlPreviewItem, type JamlPreviewSection, type JamlPreviewVisualType, } from "./utils/jamlMapPreview.js";
|
|
12
22
|
export { useMotelyStream, type StreamItem, type StreamState } from "./hooks/useShopStream.js";
|
|
23
|
+
export { useSearch, type SearchResult, type SearchStatus, type UseSearchState, } from "./hooks/useSearch.js";
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,18 @@ export { AnalyzerExplorer, } from "./components/AnalyzerExplorer.js";
|
|
|
7
7
|
export { JamlMapPreview } from "./components/JamlMapPreview.js";
|
|
8
8
|
export { JamlIde, } from "./components/JamlIde.js";
|
|
9
9
|
export { JamlIdeVisual, } from "./components/JamlIdeVisual.js";
|
|
10
|
+
export { JamlCodeEditor, } from "./components/JamlCodeEditor.js";
|
|
11
|
+
export { JimboText, } from "./ui/jimboText.js";
|
|
12
|
+
export { JimboTabs, JimboVerticalTabs, } from "./ui/jimboTabs.js";
|
|
13
|
+
export { JimboFlankNav, } from "./ui/jimboFlankNav.js";
|
|
14
|
+
export { JimboFilterBar, } from "./ui/jimboFilterBar.js";
|
|
15
|
+
export { JimboBackground } from "./ui/jimboBackground.js";
|
|
16
|
+
export { JimboTooltip, } from "./ui/jimboTooltip.js";
|
|
10
17
|
export { JamlIdeToolbar, } from "./components/JamlIdeToolbar.js";
|
|
11
18
|
export { CardList } from "./components/CardList.js";
|
|
19
|
+
export { CardFan } from "./components/CardFan.js";
|
|
20
|
+
export { DeckSprite, DECK_SPRITE_POS, STAKE_SPRITE_POS, } from "./components/DeckSprite.js";
|
|
21
|
+
export { MotelyVersionBadge, } from "./components/MotelyVersionBadge.js";
|
|
12
22
|
export { extractVisualJamlItems, } from "./utils/jamlMapPreview.js";
|
|
13
23
|
export { useMotelyStream } from "./hooks/useShopStream.js";
|
|
24
|
+
export { useSearch, } from "./hooks/useSearch.js";
|
package/dist/ui/footer.js
CHANGED
|
@@ -9,10 +9,10 @@ const SUITS = [
|
|
|
9
9
|
];
|
|
10
10
|
const CYCLE = '5s';
|
|
11
11
|
export function JimboBalatroFooter({ hidden = false, className = '' }) {
|
|
12
|
-
return (_jsxs("div", { className: ['fixed right-0 bottom-0 left-0 w-screen min-w-full transition-opacity duration-200', hidden ? 'pointer-events-none opacity-0' : 'opacity-100', className].filter(Boolean).join(' '), children: [_jsx("div", { style: { width: '100%', borderTop: '1px solid rgba(255,255,255,0.1)', background: 'rgba(0,0,0,0.9)', padding: '0 1rem 3px', textAlign: 'center' }, children: _jsxs("p", { style: { fontFamily: 'm6x11plus, monospace', fontSize: 'clamp(11px, 0.8vw + 8px, 14px)', display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center', gap: '0 0.5rem', color: 'white', margin: 0 }, children: [_jsx("span", { children: "Not affiliated with LocalThunk or PlayStack" }), _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }, children: ["Made with", ' ', _jsx("span", { style: { position: 'relative', display: 'inline-block', width: '1.5em', height: '1em', verticalAlign: 'middle' }, children: SUITS.map(({ char, kf }) => (_jsx("span", { style: { position: 'absolute', inset: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', opacity: 0, animationName: kf, animationDuration: CYCLE, animationDelay: '0s', animationIterationCount: 'infinite', animationTimingFunction: 'ease-out' }, children: char }, char))) }), ' ', "for the", ' ', _jsx("a", { href: "https://playbalatro.com", target: "_blank", rel: "noopener noreferrer", style: { color: JimboColorOption.GOLD, textDecoration: 'none' }, children: "Balatro" }), ' ', "community"] })] }) }), _jsx("style", { children: `
|
|
13
|
-
@keyframes jaml-heart { 0%{opacity:0;transform:scale(1)} 1%{opacity:1;transform:scale(1.45)} 3.5%{opacity:1;transform:scale(1)} 61.5%{opacity:1;transform:scale(1)} 62%{opacity:0} 100%{opacity:0} }
|
|
14
|
-
@keyframes jaml-spade { 0%,61.5%{opacity:0} 62%{opacity:1;transform:scale(1.45)} 64.5%{opacity:1;transform:scale(1)} 71.5%{opacity:1} 72%{opacity:0} 100%{opacity:0} }
|
|
15
|
-
@keyframes jaml-diamond { 0%,71.5%{opacity:0} 72%{opacity:1;transform:scale(1.45)} 74.5%{opacity:1;transform:scale(1)} 81.5%{opacity:1} 82%{opacity:0} 100%{opacity:0} }
|
|
16
|
-
@keyframes jaml-club { 0%,81.5%{opacity:0} 82%{opacity:1;transform:scale(1.45)} 84.5%{opacity:1;transform:scale(1)} 95%{opacity:1} 96%{opacity:0} 100%{opacity:0} }
|
|
12
|
+
return (_jsxs("div", { className: ['fixed right-0 bottom-0 left-0 w-screen min-w-full transition-opacity duration-200', hidden ? 'pointer-events-none opacity-0' : 'opacity-100', className].filter(Boolean).join(' '), children: [_jsx("div", { style: { width: '100%', borderTop: '1px solid rgba(255,255,255,0.1)', background: 'rgba(0,0,0,0.9)', padding: '0 1rem 3px', textAlign: 'center' }, children: _jsxs("p", { style: { fontFamily: 'm6x11plus, monospace', fontSize: 'clamp(11px, 0.8vw + 8px, 14px)', display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center', gap: '0 0.5rem', color: 'white', margin: 0 }, children: [_jsx("span", { children: "Not affiliated with LocalThunk or PlayStack" }), _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }, children: ["Made with", ' ', _jsx("span", { style: { position: 'relative', display: 'inline-block', width: '1.5em', height: '1em', verticalAlign: 'middle' }, children: SUITS.map(({ char, kf }) => (_jsx("span", { style: { position: 'absolute', inset: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', opacity: 0, animationName: kf, animationDuration: CYCLE, animationDelay: '0s', animationIterationCount: 'infinite', animationTimingFunction: 'ease-out' }, children: char }, char))) }), ' ', "for the", ' ', _jsx("a", { href: "https://playbalatro.com", target: "_blank", rel: "noopener noreferrer", style: { color: JimboColorOption.GOLD, textDecoration: 'none' }, children: "Balatro" }), ' ', "community"] })] }) }), _jsx("style", { children: `
|
|
13
|
+
@keyframes jaml-heart { 0%{opacity:0;transform:scale(1)} 1%{opacity:1;transform:scale(1.45)} 3.5%{opacity:1;transform:scale(1)} 61.5%{opacity:1;transform:scale(1)} 62%{opacity:0} 100%{opacity:0} }
|
|
14
|
+
@keyframes jaml-spade { 0%,61.5%{opacity:0} 62%{opacity:1;transform:scale(1.45)} 64.5%{opacity:1;transform:scale(1)} 71.5%{opacity:1} 72%{opacity:0} 100%{opacity:0} }
|
|
15
|
+
@keyframes jaml-diamond { 0%,71.5%{opacity:0} 72%{opacity:1;transform:scale(1.45)} 74.5%{opacity:1;transform:scale(1)} 81.5%{opacity:1} 82%{opacity:0} 100%{opacity:0} }
|
|
16
|
+
@keyframes jaml-club { 0%,81.5%{opacity:0} 82%{opacity:1;transform:scale(1.45)} 84.5%{opacity:1;transform:scale(1)} 95%{opacity:1} 96%{opacity:0} 100%{opacity:0} }
|
|
17
17
|
` })] }));
|
|
18
18
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fullscreen WebGL CRT/spin background — the authentic Balatro hypnotic
|
|
3
|
+
* swirl, pixelated and animated. Ported from weejoker.app's
|
|
4
|
+
* BackgroundShader.tsx; no config required.
|
|
5
|
+
*
|
|
6
|
+
* Renders a fixed-position canvas at z-index: -10 that fills the viewport
|
|
7
|
+
* and ignores pointer events. Drop it once at the root of your page:
|
|
8
|
+
*
|
|
9
|
+
* <JimboBackground />
|
|
10
|
+
* <YourAppContent />
|
|
11
|
+
*
|
|
12
|
+
* Resizes automatically. Disposes the animation frame + shader on unmount.
|
|
13
|
+
*/
|
|
14
|
+
export declare function JimboBackground(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Fullscreen WebGL CRT/spin background — the authentic Balatro hypnotic
|
|
6
|
+
* swirl, pixelated and animated. Ported from weejoker.app's
|
|
7
|
+
* BackgroundShader.tsx; no config required.
|
|
8
|
+
*
|
|
9
|
+
* Renders a fixed-position canvas at z-index: -10 that fills the viewport
|
|
10
|
+
* and ignores pointer events. Drop it once at the root of your page:
|
|
11
|
+
*
|
|
12
|
+
* <JimboBackground />
|
|
13
|
+
* <YourAppContent />
|
|
14
|
+
*
|
|
15
|
+
* Resizes automatically. Disposes the animation frame + shader on unmount.
|
|
16
|
+
*/
|
|
17
|
+
export function JimboBackground() {
|
|
18
|
+
const canvasRef = useRef(null);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const canvas = canvasRef.current;
|
|
21
|
+
if (!canvas)
|
|
22
|
+
return;
|
|
23
|
+
const gl = canvas.getContext('webgl');
|
|
24
|
+
if (!gl)
|
|
25
|
+
return;
|
|
26
|
+
const vsSource = `
|
|
27
|
+
attribute vec2 position;
|
|
28
|
+
void main() {
|
|
29
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
const fsSource = `
|
|
33
|
+
precision mediump float;
|
|
34
|
+
|
|
35
|
+
uniform float u_time;
|
|
36
|
+
uniform vec2 u_resolution;
|
|
37
|
+
|
|
38
|
+
const float SPIN_ROTATION = -2.0;
|
|
39
|
+
const float SPIN_SPEED = 4.5;
|
|
40
|
+
const vec4 COLOUR_1 = vec4(1.0, 0.2, 0.2, 1.0);
|
|
41
|
+
const vec4 COLOUR_2 = vec4(0.0, 0.5, 1.0, 1.0);
|
|
42
|
+
const vec4 COLOUR_3 = vec4(0.05, 0.08, 0.1, 1.0);
|
|
43
|
+
const float CONTRAST = 4.5;
|
|
44
|
+
const float LIGTHING = 0.5;
|
|
45
|
+
const float SPIN_AMOUNT = 0.35;
|
|
46
|
+
const float PIXEL_FILTER = 1024.0;
|
|
47
|
+
const float PI = 3.14159265359;
|
|
48
|
+
|
|
49
|
+
void main() {
|
|
50
|
+
vec2 screenSize = u_resolution;
|
|
51
|
+
float pixel_size = length(screenSize.xy) / PIXEL_FILTER;
|
|
52
|
+
vec2 uv = (floor(gl_FragCoord.xy*(1.0/pixel_size))*pixel_size - 0.5*screenSize.xy)/length(screenSize.xy);
|
|
53
|
+
float uv_len = length(uv);
|
|
54
|
+
|
|
55
|
+
float speed = (SPIN_ROTATION * 0.2) + 302.2;
|
|
56
|
+
float new_pixel_angle = atan(uv.y, uv.x) + speed - 20.0*(1.0*SPIN_AMOUNT*uv_len + (1.0 - 1.0*SPIN_AMOUNT));
|
|
57
|
+
|
|
58
|
+
vec2 mid = (screenSize.xy/length(screenSize.xy))/2.0;
|
|
59
|
+
uv = (vec2((uv_len * cos(new_pixel_angle) + mid.x), (uv_len * sin(new_pixel_angle) + mid.y)) - mid);
|
|
60
|
+
|
|
61
|
+
uv *= 30.0;
|
|
62
|
+
speed = u_time * SPIN_SPEED;
|
|
63
|
+
vec2 uv2 = vec2(uv.x, uv.y);
|
|
64
|
+
|
|
65
|
+
for(int i=0; i < 5; i++) {
|
|
66
|
+
uv2 += sin(max(uv.x, uv.y)) + uv;
|
|
67
|
+
uv += 0.5*vec2(cos(5.1123314 + 0.353*uv2.y + speed*0.131121), sin(uv2.x - 0.113*speed));
|
|
68
|
+
uv -= 1.0*cos(uv.x + uv.y) - 1.0*sin(uv.x*0.711 - uv.y);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
float contrast_mod = (0.25*CONTRAST + 0.5*SPIN_AMOUNT + 1.2);
|
|
72
|
+
float paint_res = min(2.0, max(0.0, length(uv)*(0.035)*contrast_mod));
|
|
73
|
+
float c1p = max(0.0, 1.0 - contrast_mod*abs(1.0 - paint_res));
|
|
74
|
+
float c2p = max(0.0, 1.0 - contrast_mod*abs(paint_res));
|
|
75
|
+
float c3p = 1.0 - min(1.0, c1p + c2p);
|
|
76
|
+
float light = (LIGTHING - 0.2)*max(c1p*5.0 - 4.0, 0.0) + LIGTHING*max(c2p*5.0 - 4.0, 0.0);
|
|
77
|
+
|
|
78
|
+
vec4 finalCol = (0.3/CONTRAST)*COLOUR_1 + (1.0 - 0.3/CONTRAST)*(COLOUR_1*c1p + COLOUR_2*c2p + vec4(c3p*COLOUR_3.rgb, c3p*COLOUR_1.a)) + light;
|
|
79
|
+
|
|
80
|
+
gl_FragColor = finalCol;
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
const createShader = (type, source) => {
|
|
84
|
+
const shader = gl.createShader(type);
|
|
85
|
+
if (!shader)
|
|
86
|
+
return null;
|
|
87
|
+
gl.shaderSource(shader, source);
|
|
88
|
+
gl.compileShader(shader);
|
|
89
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
90
|
+
console.error('[JimboBackground] shader compile error:', gl.getShaderInfoLog(shader));
|
|
91
|
+
gl.deleteShader(shader);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return shader;
|
|
95
|
+
};
|
|
96
|
+
const vertexShader = createShader(gl.VERTEX_SHADER, vsSource);
|
|
97
|
+
const fragmentShader = createShader(gl.FRAGMENT_SHADER, fsSource);
|
|
98
|
+
if (!vertexShader || !fragmentShader)
|
|
99
|
+
return;
|
|
100
|
+
const program = gl.createProgram();
|
|
101
|
+
if (!program)
|
|
102
|
+
return;
|
|
103
|
+
gl.attachShader(program, vertexShader);
|
|
104
|
+
gl.attachShader(program, fragmentShader);
|
|
105
|
+
gl.linkProgram(program);
|
|
106
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
107
|
+
console.error('[JimboBackground] program link error:', gl.getProgramInfoLog(program));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
gl.useProgram(program);
|
|
111
|
+
const positionBuffer = gl.createBuffer();
|
|
112
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
113
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
114
|
+
-1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
|
|
115
|
+
-1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
|
|
116
|
+
]), gl.STATIC_DRAW);
|
|
117
|
+
const positionLocation = gl.getAttribLocation(program, 'position');
|
|
118
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
119
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
120
|
+
const timeLocation = gl.getUniformLocation(program, 'u_time');
|
|
121
|
+
const resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
|
|
122
|
+
const startTime = Date.now();
|
|
123
|
+
let animationFrameId;
|
|
124
|
+
const render = () => {
|
|
125
|
+
const displayWidth = canvas.clientWidth;
|
|
126
|
+
const displayHeight = canvas.clientHeight;
|
|
127
|
+
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
|
|
128
|
+
canvas.width = displayWidth;
|
|
129
|
+
canvas.height = displayHeight;
|
|
130
|
+
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
131
|
+
}
|
|
132
|
+
const currentTime = (Date.now() - startTime) / 1000.0;
|
|
133
|
+
gl.uniform1f(timeLocation, currentTime);
|
|
134
|
+
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
|
135
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
136
|
+
animationFrameId = requestAnimationFrame(render);
|
|
137
|
+
};
|
|
138
|
+
render();
|
|
139
|
+
return () => {
|
|
140
|
+
cancelAnimationFrame(animationFrameId);
|
|
141
|
+
gl.deleteProgram(program);
|
|
142
|
+
gl.deleteShader(vertexShader);
|
|
143
|
+
gl.deleteShader(fragmentShader);
|
|
144
|
+
gl.deleteBuffer(positionBuffer);
|
|
145
|
+
};
|
|
146
|
+
}, []);
|
|
147
|
+
return (_jsx("canvas", { ref: canvasRef, "aria-hidden": true, style: {
|
|
148
|
+
position: 'fixed',
|
|
149
|
+
inset: 0,
|
|
150
|
+
width: '100%',
|
|
151
|
+
height: '100%',
|
|
152
|
+
zIndex: -10,
|
|
153
|
+
pointerEvents: 'none',
|
|
154
|
+
} }));
|
|
155
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface JimboFilterSortOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface JimboFilterBarProps {
|
|
7
|
+
search?: string;
|
|
8
|
+
onSearchChange?: (query: string) => void;
|
|
9
|
+
searchPlaceholder?: string;
|
|
10
|
+
searchLabel?: string;
|
|
11
|
+
sort?: string;
|
|
12
|
+
onSortChange?: (value: string) => void;
|
|
13
|
+
sortLabel?: string;
|
|
14
|
+
sortOptions?: JimboFilterSortOption[];
|
|
15
|
+
className?: string;
|
|
16
|
+
style?: React.CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generic Balatro-styled filter row: search input with floating pill label
|
|
20
|
+
* + optional sort dropdown with floating pill label. Adapted from
|
|
21
|
+
* weejoker's FilterBar — no hardcoded sort options, no lucide dep.
|
|
22
|
+
*
|
|
23
|
+
* Pass `sortOptions` to show the sort side; omit to show search only.
|
|
24
|
+
*/
|
|
25
|
+
export declare function JimboFilterBar({ search, onSearchChange, searchPlaceholder, searchLabel, sort, onSortChange, sortLabel, sortOptions, className, style, }: JimboFilterBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { JimboColorOption } from './tokens.js';
|
|
4
|
+
import { JimboText } from './jimboText.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generic Balatro-styled filter row: search input with floating pill label
|
|
7
|
+
* + optional sort dropdown with floating pill label. Adapted from
|
|
8
|
+
* weejoker's FilterBar — no hardcoded sort options, no lucide dep.
|
|
9
|
+
*
|
|
10
|
+
* Pass `sortOptions` to show the sort side; omit to show search only.
|
|
11
|
+
*/
|
|
12
|
+
export function JimboFilterBar({ search, onSearchChange, searchPlaceholder = 'SEARCH...', searchLabel = 'Search', sort, onSortChange, sortLabel = 'Sort By', sortOptions, className = '', style, }) {
|
|
13
|
+
return (_jsxs("div", { className: className, style: {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
gap: 24,
|
|
16
|
+
padding: 16,
|
|
17
|
+
backgroundColor: JimboColorOption.DARK_GREY,
|
|
18
|
+
border: `4px solid ${JimboColorOption.BORDER_SILVER}`,
|
|
19
|
+
boxShadow: `0 3px 0 0 ${JimboColorOption.BORDER_SOUTH}`,
|
|
20
|
+
borderRadius: 12,
|
|
21
|
+
position: 'relative',
|
|
22
|
+
flexWrap: 'wrap',
|
|
23
|
+
...style,
|
|
24
|
+
}, children: [onSearchChange ? (_jsx(FloatingLabelField, { label: searchLabel, children: _jsxs("div", { style: { position: 'relative' }, children: [_jsx("div", { style: { position: 'absolute', left: 0, top: 0, bottom: 0, width: 48, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', color: JimboColorOption.BLUE, zIndex: 1 }, children: _jsx(SearchIcon, {}) }), _jsx("input", { type: "text", value: search ?? '', onChange: (e) => onSearchChange(e.target.value), placeholder: searchPlaceholder, style: {
|
|
25
|
+
width: '100%',
|
|
26
|
+
paddingLeft: 48,
|
|
27
|
+
paddingRight: 16,
|
|
28
|
+
paddingTop: 14,
|
|
29
|
+
paddingBottom: 14,
|
|
30
|
+
backgroundColor: JimboColorOption.DARKEST,
|
|
31
|
+
border: 'none',
|
|
32
|
+
borderBottom: `4px solid ${JimboColorOption.PANEL_EDGE}`,
|
|
33
|
+
borderRadius: 8,
|
|
34
|
+
color: JimboColorOption.WHITE,
|
|
35
|
+
fontFamily: "'m6x11plus', 'Courier New', monospace",
|
|
36
|
+
fontSize: 20,
|
|
37
|
+
letterSpacing: 2,
|
|
38
|
+
textTransform: 'uppercase',
|
|
39
|
+
outline: 'none',
|
|
40
|
+
} })] }) })) : null, sortOptions && onSortChange ? (_jsx(FloatingLabelField, { label: sortLabel, children: _jsxs("div", { style: { position: 'relative' }, children: [_jsx("select", { value: sort ?? sortOptions[0]?.value, onChange: (e) => onSortChange(e.target.value), style: {
|
|
41
|
+
appearance: 'none',
|
|
42
|
+
WebkitAppearance: 'none',
|
|
43
|
+
MozAppearance: 'none',
|
|
44
|
+
backgroundColor: JimboColorOption.ORANGE,
|
|
45
|
+
color: JimboColorOption.WHITE,
|
|
46
|
+
border: 'none',
|
|
47
|
+
borderBottom: `4px solid ${JimboColorOption.DARK_ORANGE}`,
|
|
48
|
+
borderRadius: 8,
|
|
49
|
+
cursor: 'pointer',
|
|
50
|
+
fontFamily: "'m6x11plus', 'Courier New', monospace",
|
|
51
|
+
fontSize: 18,
|
|
52
|
+
letterSpacing: 2,
|
|
53
|
+
textTransform: 'uppercase',
|
|
54
|
+
padding: '14px 48px 14px 24px',
|
|
55
|
+
minWidth: 200,
|
|
56
|
+
textAlign: 'center',
|
|
57
|
+
outline: 'none',
|
|
58
|
+
}, children: sortOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) }), _jsx("div", { style: { position: 'absolute', right: 16, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', color: JimboColorOption.WHITE, opacity: 0.85 }, children: _jsx(SortIcon, {}) })] }) })) : null] }));
|
|
59
|
+
}
|
|
60
|
+
function FloatingLabelField({ label, children }) {
|
|
61
|
+
return (_jsxs("div", { style: { flex: 1, minWidth: 200, position: 'relative', marginTop: 10 }, children: [_jsx("div", { style: {
|
|
62
|
+
position: 'absolute',
|
|
63
|
+
top: -14,
|
|
64
|
+
left: 16,
|
|
65
|
+
backgroundColor: JimboColorOption.RED,
|
|
66
|
+
border: `2px solid ${JimboColorOption.DARK_RED}`,
|
|
67
|
+
borderRadius: 6,
|
|
68
|
+
padding: '4px 12px',
|
|
69
|
+
zIndex: 2,
|
|
70
|
+
}, children: _jsx(JimboText, { size: "xs", uppercase: true, children: label }) }), children] }));
|
|
71
|
+
}
|
|
72
|
+
function SearchIcon() {
|
|
73
|
+
return (_jsxs("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("circle", { cx: 11, cy: 11, r: 8 }), _jsx("line", { x1: 21, y1: 21, x2: 16.65, y2: 16.65 })] }));
|
|
74
|
+
}
|
|
75
|
+
function SortIcon() {
|
|
76
|
+
return (_jsxs("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("polyline", { points: "7 4 7 20" }), _jsx("polyline", { points: "3 8 7 4 11 8" }), _jsx("polyline", { points: "17 20 17 4" }), _jsx("polyline", { points: "21 16 17 20 13 16" })] }));
|
|
77
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface JimboFlankNavProps {
|
|
3
|
+
onPrev: () => void;
|
|
4
|
+
onNext: () => void;
|
|
5
|
+
canPrev?: boolean;
|
|
6
|
+
canNext?: boolean;
|
|
7
|
+
prevLabel?: string;
|
|
8
|
+
nextLabel?: string;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
style?: React.CSSProperties;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Prev/next navigation with flanking buttons around a central stage.
|
|
15
|
+
* Generic adaptation of weejoker's DayNavigation — no hardcoded "Day"
|
|
16
|
+
* labels, no lucide dep (inline chevron SVGs).
|
|
17
|
+
*/
|
|
18
|
+
export declare function JimboFlankNav({ onPrev, onNext, canPrev, canNext, prevLabel, nextLabel, children, className, style, }: JimboFlankNavProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { JimboColorOption, JIMBO_ANIMATIONS } from './tokens.js';
|
|
5
|
+
/**
|
|
6
|
+
* Prev/next navigation with flanking buttons around a central stage.
|
|
7
|
+
* Generic adaptation of weejoker's DayNavigation — no hardcoded "Day"
|
|
8
|
+
* labels, no lucide dep (inline chevron SVGs).
|
|
9
|
+
*/
|
|
10
|
+
export function JimboFlankNav({ onPrev, onNext, canPrev = true, canNext = true, prevLabel = 'Previous', nextLabel = 'Next', children, className = '', style, }) {
|
|
11
|
+
return (_jsxs("div", { className: className, style: {
|
|
12
|
+
display: 'flex',
|
|
13
|
+
alignItems: 'stretch',
|
|
14
|
+
justifyContent: 'center',
|
|
15
|
+
gap: 8,
|
|
16
|
+
width: '100%',
|
|
17
|
+
position: 'relative',
|
|
18
|
+
...style,
|
|
19
|
+
}, children: [_jsx(NavButton, { direction: "left", onClick: onPrev, disabled: !canPrev, "aria-label": prevLabel }), _jsx("div", { style: { position: 'relative', flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column' }, children: children }), _jsx(NavButton, { direction: "right", onClick: onNext, disabled: !canNext, "aria-label": nextLabel })] }));
|
|
20
|
+
}
|
|
21
|
+
function NavButton({ direction, onClick, disabled, 'aria-label': ariaLabel, }) {
|
|
22
|
+
const [pressed, setPressed] = React.useState(false);
|
|
23
|
+
return (_jsx("button", { type: "button", onClick: onClick, disabled: disabled, "aria-label": ariaLabel, title: ariaLabel, onMouseDown: () => !disabled && setPressed(true), onMouseUp: () => setPressed(false), onMouseLeave: () => setPressed(false), onTouchStart: () => !disabled && setPressed(true), onTouchEnd: () => setPressed(false), style: {
|
|
24
|
+
flexShrink: 0,
|
|
25
|
+
width: 48,
|
|
26
|
+
border: 'none',
|
|
27
|
+
borderRadius: 8,
|
|
28
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
29
|
+
opacity: disabled ? 0.35 : 1,
|
|
30
|
+
backgroundColor: JimboColorOption.RED,
|
|
31
|
+
color: JimboColorOption.WHITE,
|
|
32
|
+
display: 'flex',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
justifyContent: 'center',
|
|
35
|
+
transform: pressed ? `translateY(${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px)` : 'translateY(0)',
|
|
36
|
+
boxShadow: pressed ? 'none' : `0 ${JIMBO_ANIMATIONS.PRESS_TRANSLATE_Y}px 0 0 ${JimboColorOption.DARK_RED}`,
|
|
37
|
+
transition: `transform ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease, box-shadow ${JIMBO_ANIMATIONS.PRESS_DURATION}ms ease`,
|
|
38
|
+
}, children: _jsx(ChevronSvg, { direction: direction }) }));
|
|
39
|
+
}
|
|
40
|
+
function ChevronSvg({ direction }) {
|
|
41
|
+
const points = direction === 'left' ? '20,4 8,16 20,28' : '12,4 24,16 12,28';
|
|
42
|
+
return (_jsx("svg", { width: 32, height: 32, viewBox: "0 0 32 32", fill: "none", stroke: "currentColor", strokeWidth: 3, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: _jsx("polyline", { points: points }) }));
|
|
43
|
+
}
|