@inkra/react 0.0.0-stage → 0.1.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 -0
- package/README.md +213 -2
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/Yozai-OFL.txt +94 -0
- package/dist/Button.d.ts +36 -0
- package/dist/Button.js +89 -0
- package/dist/PencilIcon.d.ts +22 -0
- package/dist/PencilIcon.js +212 -0
- package/dist/PencilLine.d.ts +10 -0
- package/dist/PencilLine.js +85 -0
- package/dist/PencilSurface.d.ts +19 -0
- package/dist/PencilSurface.js +19 -0
- package/dist/PencilText.d.ts +16 -0
- package/dist/PencilText.js +205 -0
- package/dist/accordion/Accordion.d.ts +27 -0
- package/dist/accordion/Accordion.js +40 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +1 -0
- package/dist/assets/ui-hand-yozai.woff2 +0 -0
- package/dist/breadcrumb/index.d.ts +16 -0
- package/dist/breadcrumb/index.js +25 -0
- package/dist/calendar/index.d.ts +21 -0
- package/dist/calendar/index.js +41 -0
- package/dist/content/index.d.ts +106 -0
- package/dist/content/index.js +80 -0
- package/dist/day-agenda/index.d.ts +43 -0
- package/dist/day-agenda/index.js +47 -0
- package/dist/day-agenda/layout.d.ts +20 -0
- package/dist/day-agenda/layout.js +60 -0
- package/dist/display/index.d.ts +32 -0
- package/dist/display/index.js +59 -0
- package/dist/dropdown-menu/index.d.ts +34 -0
- package/dist/dropdown-menu/index.js +40 -0
- package/dist/form/EditablePencilText.d.ts +13 -0
- package/dist/form/EditablePencilText.js +312 -0
- package/dist/form/Field.d.ts +13 -0
- package/dist/form/Field.js +26 -0
- package/dist/form/Input.d.ts +27 -0
- package/dist/form/Input.js +97 -0
- package/dist/form/InputAction.d.ts +7 -0
- package/dist/form/InputAction.js +24 -0
- package/dist/form/Textarea.d.ts +5 -0
- package/dist/form/Textarea.js +23 -0
- package/dist/form/context.d.ts +12 -0
- package/dist/form/context.js +10 -0
- package/dist/form/controlProps.d.ts +23 -0
- package/dist/form/controlProps.js +20 -0
- package/dist/form/index.d.ts +7 -0
- package/dist/form/index.js +3 -0
- package/dist/form/types.d.ts +6 -0
- package/dist/form/types.js +1 -0
- package/dist/images/layers-2x.png +0 -0
- package/dist/images/layers.png +0 -0
- package/dist/images/marker-icon.png +0 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +22 -0
- package/dist/input-number/index.d.ts +16 -0
- package/dist/input-number/index.js +77 -0
- package/dist/internal/InputActionContext.d.ts +1 -0
- package/dist/internal/InputActionContext.js +3 -0
- package/dist/internal/cache.d.ts +14 -0
- package/dist/internal/cache.js +54 -0
- package/dist/internal/line.d.ts +4 -0
- package/dist/internal/line.js +21 -0
- package/dist/internal/paper.d.ts +1 -0
- package/dist/internal/paper.js +65 -0
- package/dist/internal/random.d.ts +3 -0
- package/dist/internal/random.js +30 -0
- package/dist/internal/recovery.d.ts +1 -0
- package/dist/internal/recovery.js +52 -0
- package/dist/internal/stroke.d.ts +24 -0
- package/dist/internal/stroke.js +144 -0
- package/dist/internal/surfaceRenderer.d.ts +33 -0
- package/dist/internal/surfaceRenderer.js +175 -0
- package/dist/internal/textQueue.d.ts +1 -0
- package/dist/internal/textQueue.js +37 -0
- package/dist/internal/textRenderer.d.ts +27 -0
- package/dist/internal/textRenderer.js +213 -0
- package/dist/internal/travelIcons.d.ts +24 -0
- package/dist/internal/travelIcons.js +14 -0
- package/dist/internal/useBrowserLayoutEffect.d.ts +2 -0
- package/dist/internal/useBrowserLayoutEffect.js +3 -0
- package/dist/internal/usePencilSurface.d.ts +3 -0
- package/dist/internal/usePencilSurface.js +108 -0
- package/dist/internal/useSelectionDrawing.d.ts +2 -0
- package/dist/internal/useSelectionDrawing.js +86 -0
- package/dist/internal/wash.d.ts +16 -0
- package/dist/internal/wash.js +149 -0
- package/dist/journey-map/MapScale.d.ts +4 -0
- package/dist/journey-map/MapScale.js +52 -0
- package/dist/journey-map/index.d.ts +39 -0
- package/dist/journey-map/index.js +195 -0
- package/dist/journey-map/terrain.d.ts +8 -0
- package/dist/journey-map/terrain.js +127 -0
- package/dist/overlay/Overlay.d.ts +65 -0
- package/dist/overlay/Overlay.js +77 -0
- package/dist/overlay/Popover.d.ts +43 -0
- package/dist/overlay/Popover.js +45 -0
- package/dist/overlay/index.d.ts +4 -0
- package/dist/overlay/index.js +2 -0
- package/dist/overlay/themeBridge.d.ts +8 -0
- package/dist/overlay/themeBridge.js +98 -0
- package/dist/pencil.d.ts +10 -0
- package/dist/pencil.js +39 -0
- package/dist/select/MultipleSelect.d.ts +44 -0
- package/dist/select/MultipleSelect.js +124 -0
- package/dist/select/SearchableSelect.d.ts +45 -0
- package/dist/select/SearchableSelect.js +50 -0
- package/dist/select/Select.d.ts +109 -0
- package/dist/select/Select.js +205 -0
- package/dist/select/index.d.ts +4 -0
- package/dist/select/index.js +2 -0
- package/dist/selection/Checkbox.d.ts +9 -0
- package/dist/selection/Checkbox.js +37 -0
- package/dist/selection/RadioGroup.d.ts +23 -0
- package/dist/selection/RadioGroup.js +82 -0
- package/dist/selection/Switch.d.ts +8 -0
- package/dist/selection/Switch.js +36 -0
- package/dist/selection/index.d.ts +6 -0
- package/dist/selection/index.js +3 -0
- package/dist/selection/useNativeChecked.d.ts +13 -0
- package/dist/selection/useNativeChecked.js +27 -0
- package/dist/slider/index.d.ts +11 -0
- package/dist/slider/index.js +45 -0
- package/dist/styles.css +3663 -0
- package/dist/tabs/index.d.ts +22 -0
- package/dist/tabs/index.js +42 -0
- package/dist/tokens.css +355 -0
- package/dist/tooltip/Tooltip.d.ts +24 -0
- package/dist/tooltip/Tooltip.js +32 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +1 -0
- package/package.json +100 -4
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { Button } from '../Button.js';
|
|
5
|
+
import { Popover } from '../overlay/index.js';
|
|
6
|
+
import { PencilText } from '../PencilText.js';
|
|
7
|
+
import { pencilStroke } from '../internal/stroke.js';
|
|
8
|
+
import { TravelIcon } from '../internal/travelIcons.js';
|
|
9
|
+
import { paintTerrain, paintStop, paintSea } from './terrain.js';
|
|
10
|
+
import { MapScale } from './MapScale.js';
|
|
11
|
+
import { seedToNumber } from '../internal/random.js';
|
|
12
|
+
const defaults = { zoomIn: 'Zoom in', zoomOut: 'Zoom out', reset: 'Reset view', unavailable: 'Map unavailable. Choose a place below.' };
|
|
13
|
+
const noRoutes = [];
|
|
14
|
+
const valid = (place) => Number.isFinite(place.latitude) && Number.isFinite(place.longitude) && Math.abs(place.latitude) <= 85 && Math.abs(place.longitude) <= 180;
|
|
15
|
+
export function JourneyMap({ places, routes = noRoutes, geography, landCover, water, selectedPlace, onPlaceSelect, attribution, attributionDisplay = 'inline', height = 480, interactive = true, scrollWheelZoom = false, showControls = true, showPlaceList = false, focusSelectedPlace = false, labels = defaults, className, ...props }) {
|
|
16
|
+
const root = useRef(null);
|
|
17
|
+
const host = useRef(null);
|
|
18
|
+
const mapRef = useRef(null);
|
|
19
|
+
const fitRef = useRef(() => { });
|
|
20
|
+
const paintRef = useRef(() => { });
|
|
21
|
+
const fitZoomRef = useRef(2);
|
|
22
|
+
const selectionRef = useRef(onPlaceSelect);
|
|
23
|
+
selectionRef.current = onPlaceSelect;
|
|
24
|
+
const [ready, setReady] = useState(false);
|
|
25
|
+
const [failed, setFailed] = useState(false);
|
|
26
|
+
const [placeLabels, setPlaceLabels] = useState([]);
|
|
27
|
+
const selectedRef = useRef(selectedPlace);
|
|
28
|
+
selectedRef.current = selectedPlace;
|
|
29
|
+
const markersRef = useRef([]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
let cancelled = false;
|
|
32
|
+
let cleanup;
|
|
33
|
+
setReady(false);
|
|
34
|
+
setFailed(false);
|
|
35
|
+
setPlaceLabels([]);
|
|
36
|
+
void import('leaflet').then(L => {
|
|
37
|
+
if (cancelled || !host.current)
|
|
38
|
+
return;
|
|
39
|
+
const map = L.map(host.current, { zoomControl: false, attributionControl: false, scrollWheelZoom: false, zoomSnap: .1, zoomAnimation: false, fadeAnimation: false, markerZoomAnimation: false }).setView([0, 0], 2);
|
|
40
|
+
mapRef.current = map;
|
|
41
|
+
cleanup = () => { map.remove(); mapRef.current = null; markersRef.current = []; };
|
|
42
|
+
const points = places.filter(valid);
|
|
43
|
+
const boundaries = [];
|
|
44
|
+
if (geography) {
|
|
45
|
+
const layer = L.geoJSON(geography);
|
|
46
|
+
const collect = (item) => {
|
|
47
|
+
if (item instanceof L.Polyline) {
|
|
48
|
+
const flatten = (values) => {
|
|
49
|
+
if (values[0] instanceof L.LatLng) {
|
|
50
|
+
const ring = values;
|
|
51
|
+
boundaries.push(item instanceof L.Polygon ? [...ring, ring[0]] : ring);
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
values.forEach(value => { if (Array.isArray(value))
|
|
55
|
+
flatten(value); });
|
|
56
|
+
};
|
|
57
|
+
flatten(item.getLatLngs());
|
|
58
|
+
}
|
|
59
|
+
else if (item instanceof L.LayerGroup)
|
|
60
|
+
item.eachLayer(collect);
|
|
61
|
+
};
|
|
62
|
+
layer.eachLayer(collect);
|
|
63
|
+
}
|
|
64
|
+
const routePoints = routes.map(route => route.coordinates.filter(([lat, lng]) => Number.isFinite(lat) && Number.isFinite(lng) && Math.abs(lat) <= 85 && Math.abs(lng) <= 180).map(([lat, lng]) => L.latLng(lat, lng)));
|
|
65
|
+
const all = [...points.map(place => L.latLng(place.latitude, place.longitude)), ...boundaries.flat(), ...routePoints.flat()];
|
|
66
|
+
let fitted = false;
|
|
67
|
+
const fit = () => {
|
|
68
|
+
map.invalidateSize();
|
|
69
|
+
const size = map.getSize();
|
|
70
|
+
if (size.x <= 0 || size.y <= 0)
|
|
71
|
+
return;
|
|
72
|
+
if (all.length)
|
|
73
|
+
map.fitBounds(L.latLngBounds(all), { padding: [44, 44], maxZoom: 12, animate: false });
|
|
74
|
+
fitZoomRef.current = map.getZoom();
|
|
75
|
+
fitted = true;
|
|
76
|
+
};
|
|
77
|
+
fitRef.current = fit;
|
|
78
|
+
const canvas = document.createElement('canvas');
|
|
79
|
+
canvas.className = 'inkra-journey-map__ink';
|
|
80
|
+
canvas.setAttribute('aria-hidden', 'true');
|
|
81
|
+
host.current.append(canvas);
|
|
82
|
+
let frame = 0;
|
|
83
|
+
let paintOrigin = map.containerPointToLatLng([0, 0]), paintZoom = map.getZoom();
|
|
84
|
+
const paint = () => {
|
|
85
|
+
frame = 0;
|
|
86
|
+
const { x: width, y: height } = map.getSize();
|
|
87
|
+
if (!width || !height)
|
|
88
|
+
return;
|
|
89
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
90
|
+
canvas.width = Math.round(width * dpr);
|
|
91
|
+
canvas.height = Math.round(height * dpr);
|
|
92
|
+
canvas.style.width = `${width}px`;
|
|
93
|
+
canvas.style.height = `${height}px`;
|
|
94
|
+
const context = canvas.getContext('2d');
|
|
95
|
+
if (!context)
|
|
96
|
+
return;
|
|
97
|
+
canvas.style.transform = '';
|
|
98
|
+
paintOrigin = map.containerPointToLatLng([0, 0]);
|
|
99
|
+
paintZoom = map.getZoom();
|
|
100
|
+
context.scale(dpr, dpr);
|
|
101
|
+
const style = getComputedStyle(host.current);
|
|
102
|
+
const paper = style.getPropertyValue('--inkra-paper').trim();
|
|
103
|
+
const dark = Number.parseInt(paper.replace('#', '').slice(0, 2), 16) < 100;
|
|
104
|
+
const project = (position) => { const point = map.latLngToContainerPoint([position[1], position[0]]); return [point.x, point.y]; };
|
|
105
|
+
paintSea(context, width, height, project([0, 0]));
|
|
106
|
+
if (geography)
|
|
107
|
+
paintTerrain(context, geography, project, width, height, dark);
|
|
108
|
+
if (landCover)
|
|
109
|
+
paintTerrain(context, landCover, project, width, height, dark);
|
|
110
|
+
if (water)
|
|
111
|
+
paintTerrain(context, water, project, width, height, dark, 'water');
|
|
112
|
+
const draw = (line, color, seed, weight) => {
|
|
113
|
+
const projected = line.map(point => { const p = map.latLngToContainerPoint(point); return [p.x, p.y]; });
|
|
114
|
+
pencilStroke(context, projected, color, weight, seed, .3, 3, false, { grain: .7, breaks: .15, variation: .8, gain: 2.5, step: .8 });
|
|
115
|
+
};
|
|
116
|
+
boundaries.forEach((line, i) => draw(line, style.getPropertyValue('--inkra-border-strong').trim(), i + 137, 1.2));
|
|
117
|
+
routePoints.forEach((line, i) => draw(line, style.getPropertyValue('--inkra-accent').trim(), i + 717, 2));
|
|
118
|
+
points.forEach(place => {
|
|
119
|
+
const p = map.latLngToContainerPoint([place.latitude, place.longitude]);
|
|
120
|
+
paintStop(context, p.x, p.y, seedToNumber(place.id), style.getPropertyValue('--inkra-accent-strong').trim(), paper, selectedRef.current === place.id);
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
const schedule = () => { if (!frame)
|
|
124
|
+
frame = requestAnimationFrame(paint); };
|
|
125
|
+
paintRef.current = schedule;
|
|
126
|
+
// Reuse the existing raster during a pan; repaint geographical detail on rest.
|
|
127
|
+
const translate = () => {
|
|
128
|
+
const point = map.latLngToContainerPoint(paintOrigin);
|
|
129
|
+
canvas.style.transformOrigin = '0 0';
|
|
130
|
+
canvas.style.transform = `translate(${point.x}px, ${point.y}px) scale(${map.getZoomScale(map.getZoom(), paintZoom)})`;
|
|
131
|
+
};
|
|
132
|
+
const settle = () => { canvas.style.transform = ''; paintOrigin = map.containerPointToLatLng([0, 0]); paintZoom = map.getZoom(); schedule(); };
|
|
133
|
+
map.on('move', translate);
|
|
134
|
+
map.on('moveend zoomend resize', settle);
|
|
135
|
+
map.on('dragstart', () => map.stop());
|
|
136
|
+
const labelNodes = [];
|
|
137
|
+
markersRef.current = points.map(place => {
|
|
138
|
+
const node = document.createElement('span');
|
|
139
|
+
node.className = 'inkra-journey-map__pin';
|
|
140
|
+
labelNodes.push({ place, node });
|
|
141
|
+
const marker = L.marker([place.latitude, place.longitude], { icon: L.divIcon({ html: node, className: 'inkra-journey-map__marker', iconSize: [44, 44], iconAnchor: [22, 22] }), keyboard: true, title: place.label, alt: place.label }).addTo(map);
|
|
142
|
+
marker.on('click', () => selectionRef.current?.(place));
|
|
143
|
+
marker.getElement()?.setAttribute('aria-pressed', String(selectedRef.current === place.id));
|
|
144
|
+
return { place, marker };
|
|
145
|
+
});
|
|
146
|
+
setPlaceLabels(labelNodes);
|
|
147
|
+
const observer = new ResizeObserver(() => { map.invalidateSize(); if (!fitted)
|
|
148
|
+
fit(); schedule(); });
|
|
149
|
+
observer.observe(host.current);
|
|
150
|
+
const appearance = new MutationObserver(schedule);
|
|
151
|
+
appearance.observe(document.documentElement, { subtree: true, attributes: true, attributeFilter: ['data-theme', 'data-inkra-theme'] });
|
|
152
|
+
const scheme = matchMedia('(prefers-color-scheme: dark)');
|
|
153
|
+
scheme.addEventListener('change', schedule);
|
|
154
|
+
fit();
|
|
155
|
+
schedule();
|
|
156
|
+
setReady(true);
|
|
157
|
+
cleanup = () => { cancelAnimationFrame(frame); observer.disconnect(); appearance.disconnect(); scheme.removeEventListener('change', schedule); map.remove(); canvas.remove(); mapRef.current = null; markersRef.current = []; paintRef.current = () => { }; };
|
|
158
|
+
}).catch(() => { if (!cancelled) {
|
|
159
|
+
setFailed(true);
|
|
160
|
+
cleanup?.();
|
|
161
|
+
cleanup = undefined;
|
|
162
|
+
} });
|
|
163
|
+
return () => { cancelled = true; cleanup?.(); };
|
|
164
|
+
}, [places, routes, geography, landCover, water]);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
markersRef.current.forEach(({ place, marker }) => marker.getElement()?.setAttribute('aria-pressed', String(selectedPlace === place.id)));
|
|
167
|
+
paintRef.current();
|
|
168
|
+
const place = markersRef.current.find(item => item.place.id === selectedPlace)?.place;
|
|
169
|
+
if (focusSelectedPlace && place) {
|
|
170
|
+
const map = mapRef.current;
|
|
171
|
+
map?.stop();
|
|
172
|
+
const targetZoom = fitZoomRef.current + Math.log2(10);
|
|
173
|
+
if (matchMedia('(prefers-reduced-motion: reduce)').matches)
|
|
174
|
+
map?.setView([place.latitude, place.longitude], targetZoom, { animate: false });
|
|
175
|
+
else
|
|
176
|
+
map?.flyTo([place.latitude, place.longitude], targetZoom, { duration: .85 });
|
|
177
|
+
}
|
|
178
|
+
}, [selectedPlace, focusSelectedPlace, ready]);
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
const map = mapRef.current;
|
|
181
|
+
if (!map || !ready)
|
|
182
|
+
return;
|
|
183
|
+
for (const handler of [map.dragging, map.touchZoom, map.doubleClickZoom, map.boxZoom, map.keyboard]) {
|
|
184
|
+
if (interactive)
|
|
185
|
+
handler.enable();
|
|
186
|
+
else
|
|
187
|
+
handler.disable();
|
|
188
|
+
}
|
|
189
|
+
if (interactive && scrollWheelZoom)
|
|
190
|
+
map.scrollWheelZoom.enable();
|
|
191
|
+
else
|
|
192
|
+
map.scrollWheelZoom.disable();
|
|
193
|
+
}, [interactive, scrollWheelZoom, ready]);
|
|
194
|
+
return _jsxs("div", { ...props, ref: root, className: ['inkra-journey-map', className].filter(Boolean).join(' '), children: [placeLabels.map(({ place, node }) => createPortal(_jsx(PencilText, { children: place.label }), node, place.id)), _jsxs("div", { className: "inkra-journey-map__viewport", style: { height: Math.max(240, Math.min(800, height)) }, children: [attributionDisplay === 'popover' && _jsx("div", { className: "inkra-journey-map__credits", children: _jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { size: "icon", variant: "ghost", "aria-label": labels.attribution ?? 'Map credits', title: labels.attribution ?? 'Map credits', children: _jsx(TravelIcon, { name: "info" }) }) }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "end", sideOffset: 8, collisionPadding: 20, className: "inkra-journey-map__credits-content", children: [_jsx(Popover.Title, { children: labels.attribution ?? 'Map credits' }), _jsx(Popover.Description, { children: attribution })] }) })] }) }), _jsx("div", { ref: host, className: "inkra-journey-map__host", "aria-label": props['aria-label'] ?? 'Map' }), showControls && _jsxs("div", { className: "inkra-journey-map__tools", children: [_jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.zoomIn, title: labels.zoomIn, onClick: () => { mapRef.current?.stop(); mapRef.current?.zoomIn(); }, children: _jsx(TravelIcon, { name: "plus" }) }), _jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.zoomOut, title: labels.zoomOut, onClick: () => { mapRef.current?.stop(); mapRef.current?.zoomOut(); }, children: _jsx(TravelIcon, { name: "minus" }) }), _jsx(Button, { size: "icon", variant: "ghost", disabled: !ready, "aria-label": labels.reset, title: labels.reset, onClick: () => { mapRef.current?.stop(); fitRef.current(); }, children: _jsx(TravelIcon, { name: "reset" }) })] }), ready && _jsx(MapScale, { map: mapRef.current }), failed && _jsx("p", { role: "status", children: labels.unavailable })] }), attributionDisplay === 'inline' && _jsx("p", { className: "inkra-journey-map__attribution", children: attribution }), (showPlaceList || !ready || failed) && _jsx("ol", { className: "inkra-journey-map__places", children: places.filter(valid).map((place, index) => _jsx("li", { children: _jsxs("button", { type: "button", "aria-pressed": selectedPlace === place.id, onClick: () => { mapRef.current?.panTo([place.latitude, place.longitude], { animate: false }); onPlaceSelect?.(place); }, children: [_jsx("span", { "aria-hidden": "true", children: index + 1 }), _jsx(PencilText, { children: place.label })] }) }, place.id)) })] });
|
|
195
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GeoJsonObject, Feature, Position } from 'geojson';
|
|
2
|
+
import { type PencilPoint } from '../internal/stroke.js';
|
|
3
|
+
type Project = (position: Position) => PencilPoint;
|
|
4
|
+
export declare function paintSea(ctx: CanvasRenderingContext2D, width: number, height: number, anchor: PencilPoint): void;
|
|
5
|
+
export declare function mapFeatures(data: GeoJsonObject): Feature[];
|
|
6
|
+
export declare function paintTerrain(ctx: CanvasRenderingContext2D, data: GeoJsonObject, project: Project, width: number, height: number, dark: boolean, defaultKind?: string): void;
|
|
7
|
+
export declare function paintStop(ctx: CanvasRenderingContext2D, x: number, y: number, seed: number, color: string, paper: string, selected: boolean): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { pencilStroke } from '../internal/stroke.js';
|
|
2
|
+
import { seededRandom } from '../internal/random.js';
|
|
3
|
+
// Palette, anchored hatch and category glyphs adapted from adventure/pencil/brush.js.
|
|
4
|
+
const palette = {
|
|
5
|
+
land: ['#e6e2af', '#83924a'], forest: ['#a7c7ad', '#3c7356'], crop: ['#e6c17b', '#9c7536'],
|
|
6
|
+
shrub: ['#c8c38f', '#797742'], bare: ['#cec4d9', '#83708e'], snow: ['#f4f5f1', '#94afb7'],
|
|
7
|
+
wetland: ['#a6c7c6', '#437e82'], urban: ['#d49b8b', '#975b4f'], water: ['#87bbc0', '#4d91a0'],
|
|
8
|
+
};
|
|
9
|
+
const stroke = (ctx, points, color, seed, width = .6) => pencilStroke(ctx, points, color, width, seed, .3, 1, false, { variation: 1, breaks: .45, grain: .78, gain: 1.7, step: 1 });
|
|
10
|
+
let seaTile;
|
|
11
|
+
export function paintSea(ctx, width, height, anchor) {
|
|
12
|
+
if (!seaTile) {
|
|
13
|
+
seaTile = document.createElement('canvas');
|
|
14
|
+
seaTile.width = seaTile.height = 512;
|
|
15
|
+
const ink = seaTile.getContext('2d');
|
|
16
|
+
if (!ink) {
|
|
17
|
+
seaTile = undefined;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const rng = seededRandom(4801);
|
|
21
|
+
for (let i = 0; i < 320; i++) {
|
|
22
|
+
const x = rng() * 512, y = rng() * 512, length = 9 + rng() * 49;
|
|
23
|
+
ink.globalAlpha = .1 + rng() * .2;
|
|
24
|
+
for (const [dx, dy] of [[0, 0], [-512, 0], [0, 512], [-512, 512]]) {
|
|
25
|
+
ink.save();
|
|
26
|
+
ink.translate(dx, dy);
|
|
27
|
+
stroke(ink, [[x, y], [x + length * .5, y - length * .09], [x + length, y - length * .18]], '#4d91a0', 8100 + i, .5 + i % 4 * .1);
|
|
28
|
+
ink.restore();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const pattern = ctx.createPattern(seaTile, 'repeat');
|
|
33
|
+
if (!pattern)
|
|
34
|
+
return;
|
|
35
|
+
pattern.setTransform(new DOMMatrix().translate(anchor[0], anchor[1]));
|
|
36
|
+
ctx.save();
|
|
37
|
+
ctx.fillStyle = pattern;
|
|
38
|
+
ctx.fillRect(0, 0, width, height);
|
|
39
|
+
ctx.restore();
|
|
40
|
+
}
|
|
41
|
+
export function mapFeatures(data) {
|
|
42
|
+
if (data.type === 'FeatureCollection')
|
|
43
|
+
return data.features;
|
|
44
|
+
if (data.type === 'Feature')
|
|
45
|
+
return [data];
|
|
46
|
+
return [{ type: 'Feature', properties: {}, geometry: data }];
|
|
47
|
+
}
|
|
48
|
+
export function paintTerrain(ctx, data, project, width, height, dark, defaultKind = 'land') {
|
|
49
|
+
for (const [featureIndex, feature] of mapFeatures(data).entries()) {
|
|
50
|
+
const geometry = feature.geometry;
|
|
51
|
+
if (!geometry)
|
|
52
|
+
continue;
|
|
53
|
+
const polygons = geometry.type === 'MultiPolygon' ? geometry.coordinates : geometry.type === 'Polygon' ? [geometry.coordinates] : [];
|
|
54
|
+
const kind = String(feature.properties?.id ?? defaultKind);
|
|
55
|
+
const [fill, ink] = palette[kind] ?? palette.land;
|
|
56
|
+
for (const [polygonIndex, polygon] of polygons.entries()) {
|
|
57
|
+
const rings = polygon.map(ring => ring.map(project));
|
|
58
|
+
const points = rings.flat();
|
|
59
|
+
if (!points.length)
|
|
60
|
+
continue;
|
|
61
|
+
const left = Math.max(0, Math.min(...points.map(p => p[0]))), right = Math.min(width, Math.max(...points.map(p => p[0])));
|
|
62
|
+
const top = Math.max(0, Math.min(...points.map(p => p[1]))), bottom = Math.min(height, Math.max(...points.map(p => p[1])));
|
|
63
|
+
if (left >= right || top >= bottom)
|
|
64
|
+
continue;
|
|
65
|
+
ctx.save();
|
|
66
|
+
ctx.beginPath();
|
|
67
|
+
for (const ring of rings) {
|
|
68
|
+
ring.forEach(([x, y], index) => index ? ctx.lineTo(x, y) : ctx.moveTo(x, y));
|
|
69
|
+
ctx.closePath();
|
|
70
|
+
}
|
|
71
|
+
ctx.clip('evenodd');
|
|
72
|
+
if (defaultKind === 'land' && kind === 'land') {
|
|
73
|
+
ctx.fillStyle = dark ? '#191d1e' : '#fdfcf8';
|
|
74
|
+
ctx.fillRect(left, top, right - left, bottom - top);
|
|
75
|
+
}
|
|
76
|
+
ctx.fillStyle = fill;
|
|
77
|
+
ctx.globalAlpha = dark ? .16 : .45;
|
|
78
|
+
ctx.fillRect(left, top, right - left, bottom - top);
|
|
79
|
+
ctx.globalAlpha = dark ? .75 : .65;
|
|
80
|
+
// Anchor the sampling lattice to geographic coordinates, never the viewport.
|
|
81
|
+
const origin = project([0, 0]);
|
|
82
|
+
const spacing = kind === 'forest' ? 9 : 13;
|
|
83
|
+
const seed = 21000 + featureIndex * 43 + polygonIndex;
|
|
84
|
+
for (let row = Math.floor((top - origin[1]) / spacing); row <= Math.ceil((bottom - origin[1]) / spacing); row++) {
|
|
85
|
+
for (let col = Math.floor((left - origin[0]) / 36); col <= Math.ceil((right - origin[0]) / 36); col++) {
|
|
86
|
+
const cell = (seed ^ Math.imul(col, 73856093) ^ Math.imul(row, 19349663)) >>> 0;
|
|
87
|
+
const rng = seededRandom(cell);
|
|
88
|
+
if (rng() < .2)
|
|
89
|
+
continue;
|
|
90
|
+
const x = origin[0] + col * 36 + rng() * 12, y = origin[1] + row * spacing + rng() * 5;
|
|
91
|
+
const length = 14 + rng() * 30;
|
|
92
|
+
stroke(ctx, [[x, y], [x + length * .45, y - length * .14], [x + length, y - length * .28]], dark ? fill : ink, cell, .65);
|
|
93
|
+
if (kind === 'forest')
|
|
94
|
+
stroke(ctx, [[x - 3, y], [x, y - 7], [x + 3, y], [x, y - 4], [x, y + 2]], dark ? fill : ink, cell + 1);
|
|
95
|
+
else if (kind === 'bare')
|
|
96
|
+
stroke(ctx, [[x - 5, y + 3], [x, y - 5], [x + 5, y + 3]], dark ? fill : ink, cell + 1);
|
|
97
|
+
else if (kind === 'crop')
|
|
98
|
+
stroke(ctx, [[x - 3, y - 3], [x + 5, y - 1], [x + 4, y + 3]], dark ? fill : ink, cell + 1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
ctx.restore();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// The original stopMarker.js uses an irregular ring and separate pencil fill passes.
|
|
106
|
+
export function paintStop(ctx, x, y, seed, color, paper, selected) {
|
|
107
|
+
const ring = (radius) => Array.from({ length: 73 }, (_, i) => {
|
|
108
|
+
const angle = i / 72 * Math.PI * 2;
|
|
109
|
+
const r = radius + Math.sin(angle * 3 + seed * .71) * .48 + Math.sin(angle * 5 - seed * .71) * .24;
|
|
110
|
+
return [x + Math.cos(angle) * r, y + Math.sin(angle) * r * .95];
|
|
111
|
+
});
|
|
112
|
+
const outline = ring(11);
|
|
113
|
+
ctx.save();
|
|
114
|
+
ctx.beginPath();
|
|
115
|
+
outline.forEach(([px, py], i) => i ? ctx.lineTo(px, py) : ctx.moveTo(px, py));
|
|
116
|
+
ctx.closePath();
|
|
117
|
+
ctx.fillStyle = paper;
|
|
118
|
+
ctx.fill();
|
|
119
|
+
for (let row = -9; row <= 9; row += 1.6) {
|
|
120
|
+
const half = Math.sqrt(Math.max(0, 81 - row * row));
|
|
121
|
+
stroke(ctx, [[x - half, y + row + .7], [x + half, y + row - .7]], color, seed + row * 13, 1.4);
|
|
122
|
+
}
|
|
123
|
+
pencilStroke(ctx, outline, color, 1.4, seed, .3, 3, true, { variation: .8, breaks: .18, grain: .66, gain: 2.7, step: .5 });
|
|
124
|
+
if (selected)
|
|
125
|
+
pencilStroke(ctx, ring(16), color, 1.4, seed + 29, .3, 3, true, { variation: .8, breaks: .3, grain: .66, gain: 2.7, step: .5 });
|
|
126
|
+
ctx.restore();
|
|
127
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
2
|
+
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
3
|
+
import type { ButtonProps } from '../Button.js';
|
|
4
|
+
import type { PencilSurfaceVariant } from '../PencilSurface.js';
|
|
5
|
+
export type OverlayRootProps = DialogPrimitive.DialogProps;
|
|
6
|
+
export declare function OverlayRoot(props: OverlayRootProps): import("react").JSX.Element;
|
|
7
|
+
export type OverlayTriggerProps = DialogPrimitive.DialogTriggerProps;
|
|
8
|
+
export declare const OverlayTrigger: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
9
|
+
export type OverlayPortalProps = DialogPrimitive.DialogPortalProps;
|
|
10
|
+
export declare function OverlayPortal({ children, ...props }: OverlayPortalProps): import("react").JSX.Element;
|
|
11
|
+
export type OverlayBackdropProps = DialogPrimitive.DialogOverlayProps;
|
|
12
|
+
export declare const OverlayBackdrop: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogOverlayProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
13
|
+
export interface OverlayContentProps extends DialogPrimitive.DialogContentProps {
|
|
14
|
+
/** Plain preserves the padded legacy content; sections fixes Header/Footer around a scrolling Body. */
|
|
15
|
+
layout?: 'plain' | 'sections';
|
|
16
|
+
seed?: string | number;
|
|
17
|
+
surfaceVariant?: PencilSurfaceVariant;
|
|
18
|
+
surfaceRadius?: number;
|
|
19
|
+
surfaceClassName?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const OverlayContent: import("react").ForwardRefExoticComponent<OverlayContentProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
22
|
+
export interface OverlayTitleProps extends Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Title>, 'children'> {
|
|
23
|
+
children: string;
|
|
24
|
+
seed?: string | number;
|
|
25
|
+
}
|
|
26
|
+
export declare const OverlayTitle: import("react").ForwardRefExoticComponent<OverlayTitleProps & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
27
|
+
export interface OverlayDescriptionProps extends Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Description>, 'children'> {
|
|
28
|
+
children: string;
|
|
29
|
+
seed?: string | number;
|
|
30
|
+
}
|
|
31
|
+
export declare const OverlayDescription: import("react").ForwardRefExoticComponent<OverlayDescriptionProps & import("react").RefAttributes<HTMLParagraphElement>>;
|
|
32
|
+
export type OverlayCloseProps = ComponentPropsWithoutRef<typeof DialogPrimitive.Close>;
|
|
33
|
+
export declare const OverlayClose: import("react").ForwardRefExoticComponent<Omit<DialogPrimitive.DialogCloseProps & import("react").RefAttributes<HTMLButtonElement>, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
34
|
+
export interface OverlayCloseButtonProps extends Omit<ButtonProps, 'children' | 'size' | 'variant' | 'loading' | 'aria-label' | 'type'> {
|
|
35
|
+
label?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare const OverlayCloseButton: import("react").ForwardRefExoticComponent<OverlayCloseButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
38
|
+
export interface OverlayHeaderProps extends ComponentPropsWithoutRef<'div'> {
|
|
39
|
+
divided?: boolean;
|
|
40
|
+
showClose?: boolean;
|
|
41
|
+
closeLabel?: string;
|
|
42
|
+
action?: ReactNode;
|
|
43
|
+
}
|
|
44
|
+
export declare const OverlayHeader: import("react").ForwardRefExoticComponent<OverlayHeaderProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
45
|
+
export type OverlayBodyProps = ComponentPropsWithoutRef<'div'>;
|
|
46
|
+
export declare const OverlayBody: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
47
|
+
export interface OverlayFooterProps extends ComponentPropsWithoutRef<'div'> {
|
|
48
|
+
divided?: boolean;
|
|
49
|
+
align?: 'start' | 'center' | 'end';
|
|
50
|
+
}
|
|
51
|
+
export declare const OverlayFooter: import("react").ForwardRefExoticComponent<OverlayFooterProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
52
|
+
export declare const Overlay: Readonly<{
|
|
53
|
+
Root: typeof OverlayRoot;
|
|
54
|
+
Trigger: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
55
|
+
Portal: typeof OverlayPortal;
|
|
56
|
+
Backdrop: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogOverlayProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
57
|
+
Content: import("react").ForwardRefExoticComponent<OverlayContentProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
58
|
+
Header: import("react").ForwardRefExoticComponent<OverlayHeaderProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
59
|
+
Body: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
60
|
+
Footer: import("react").ForwardRefExoticComponent<OverlayFooterProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
61
|
+
CloseButton: import("react").ForwardRefExoticComponent<OverlayCloseButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
62
|
+
Title: import("react").ForwardRefExoticComponent<OverlayTitleProps & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
63
|
+
Description: import("react").ForwardRefExoticComponent<OverlayDescriptionProps & import("react").RefAttributes<HTMLParagraphElement>>;
|
|
64
|
+
Close: import("react").ForwardRefExoticComponent<Omit<DialogPrimitive.DialogCloseProps & import("react").RefAttributes<HTMLButtonElement>, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
65
|
+
}>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
|
+
import { forwardRef, useId } from 'react';
|
|
4
|
+
import { __iconData as closeIcon } from 'lucide-react/dist/esm/icons/x.mjs';
|
|
5
|
+
import { Button } from '../Button.js';
|
|
6
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
7
|
+
import { PencilLine } from '../PencilLine.js';
|
|
8
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
9
|
+
import { PencilText } from '../PencilText.js';
|
|
10
|
+
import { Tooltip } from '../tooltip/Tooltip.js';
|
|
11
|
+
import { PortalThemeBridge, PortalThemeProvider, usePortalTriggerRef, } from './themeBridge.js';
|
|
12
|
+
function classes(...values) {
|
|
13
|
+
return values.filter(Boolean).join(' ');
|
|
14
|
+
}
|
|
15
|
+
export function OverlayRoot(props) {
|
|
16
|
+
return (_jsx(PortalThemeProvider, { children: _jsx(DialogPrimitive.Root, { ...props }) }));
|
|
17
|
+
}
|
|
18
|
+
export const OverlayTrigger = forwardRef(function OverlayTrigger(props, forwardedRef) {
|
|
19
|
+
const ref = usePortalTriggerRef(forwardedRef);
|
|
20
|
+
return _jsx(DialogPrimitive.Trigger, { ...props, ref: ref });
|
|
21
|
+
});
|
|
22
|
+
export function OverlayPortal({ children, ...props }) {
|
|
23
|
+
return (_jsx(DialogPrimitive.Portal, { ...props, children: _jsx(PortalThemeBridge, { children: children }) }));
|
|
24
|
+
}
|
|
25
|
+
export const OverlayBackdrop = forwardRef(function OverlayBackdrop({ className, ...props }, ref) {
|
|
26
|
+
return (_jsx(DialogPrimitive.Overlay, { ...props, className: classes('inkra-overlay__backdrop', className), ref: ref }));
|
|
27
|
+
});
|
|
28
|
+
export const OverlayContent = forwardRef(function OverlayContent({ children, className, layout = 'plain', seed, surfaceClassName, surfaceRadius = 8, surfaceVariant = 'paper', ...props }, ref) {
|
|
29
|
+
const id = useId();
|
|
30
|
+
return (_jsx(DialogPrimitive.Content, { ...props, className: classes('inkra-overlay__content', className), "data-layout": layout, ref: ref, children: _jsx(PencilSurface, { className: classes('inkra-overlay__surface', surfaceClassName), radius: surfaceRadius, seed: seed ?? `inkra-overlay-${id}`, variant: surfaceVariant, children: _jsx("div", { className: "inkra-overlay__content-body", children: children }) }) }));
|
|
31
|
+
});
|
|
32
|
+
export const OverlayTitle = forwardRef(function OverlayTitle({ children, className, seed, ...props }, ref) {
|
|
33
|
+
const id = useId();
|
|
34
|
+
return (_jsx(DialogPrimitive.Title, { ...props, className: classes('inkra-overlay__title', className), ref: ref, children: _jsx(PencilText, { seed: seed ?? `inkra-overlay-title-${id}`, children: children }) }));
|
|
35
|
+
});
|
|
36
|
+
export const OverlayDescription = forwardRef(function OverlayDescription({ children, className, seed, ...props }, ref) {
|
|
37
|
+
const id = useId();
|
|
38
|
+
return (_jsx(DialogPrimitive.Description, { ...props, className: classes('inkra-overlay__description', className), ref: ref, children: _jsx(PencilText, { seed: seed ?? `inkra-overlay-description-${id}`, children: children }) }));
|
|
39
|
+
});
|
|
40
|
+
export const OverlayClose = forwardRef(function OverlayClose(props, ref) {
|
|
41
|
+
return _jsx(DialogPrimitive.Close, { ...props, ref: ref });
|
|
42
|
+
});
|
|
43
|
+
const closePaths = closeIcon.node.map(([, attributes]) => ({ d: attributes.d }));
|
|
44
|
+
export const OverlayCloseButton = forwardRef(function OverlayCloseButton({ label = 'Close', className, onFocus, ...props }, ref) {
|
|
45
|
+
return _jsx(Tooltip.Provider, { children: _jsxs(Tooltip.Root, { children: [_jsx(Tooltip.Trigger, { asChild: true, children: _jsx(DialogPrimitive.Close, { asChild: true, children: _jsx(Button, { ...props, ref: ref, type: "button", size: "icon", variant: "ghost", className: classes('inkra-overlay__close-button', className), "aria-label": label, onFocus: event => {
|
|
46
|
+
onFocus?.(event);
|
|
47
|
+
const dialog = event.currentTarget.closest('[role="dialog"]');
|
|
48
|
+
// Initial dialog focus must not open a tooltip that consumes the first Escape.
|
|
49
|
+
if (dialog && (!event.relatedTarget || !dialog.contains(event.relatedTarget)))
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
}, children: _jsx(PencilIcon, { paths: closePaths, size: 30, sourceSize: 24, decorative: true }) }) }) }), _jsx(Tooltip.Portal, { children: _jsx(Tooltip.Content, { side: "bottom", align: "end", children: label }) })] }) });
|
|
52
|
+
});
|
|
53
|
+
export const OverlayHeader = forwardRef(function OverlayHeader({ children, className, divided = true, showClose = true, closeLabel = 'Close', action, ...props }, ref) {
|
|
54
|
+
const id = useId();
|
|
55
|
+
return _jsxs("div", { ...props, ref: ref, className: classes('inkra-overlay__header', className), "data-divided": divided || undefined, children: [_jsx("div", { className: "inkra-overlay__heading", children: children }), (action || showClose) && _jsxs("div", { className: "inkra-overlay__header-actions", children: [action, showClose && _jsx(OverlayCloseButton, { label: closeLabel })] }), divided && _jsx(PencilLine, { className: "inkra-overlay__section-divider", seed: `overlay-header-${id}` })] });
|
|
56
|
+
});
|
|
57
|
+
export const OverlayBody = forwardRef(function OverlayBody({ className, ...props }, ref) {
|
|
58
|
+
return _jsx("div", { ...props, ref: ref, className: classes('inkra-overlay__body', className) });
|
|
59
|
+
});
|
|
60
|
+
export const OverlayFooter = forwardRef(function OverlayFooter({ children, className, divided = true, align = 'end', ...props }, ref) {
|
|
61
|
+
const id = useId();
|
|
62
|
+
return _jsxs("div", { ...props, ref: ref, className: classes('inkra-overlay__footer', className), "data-divided": divided || undefined, "data-align": align, children: [divided && _jsx(PencilLine, { className: "inkra-overlay__section-divider", seed: `overlay-footer-${id}` }), children] });
|
|
63
|
+
});
|
|
64
|
+
export const Overlay = Object.freeze({
|
|
65
|
+
Root: OverlayRoot,
|
|
66
|
+
Trigger: OverlayTrigger,
|
|
67
|
+
Portal: OverlayPortal,
|
|
68
|
+
Backdrop: OverlayBackdrop,
|
|
69
|
+
Content: OverlayContent,
|
|
70
|
+
Header: OverlayHeader,
|
|
71
|
+
Body: OverlayBody,
|
|
72
|
+
Footer: OverlayFooter,
|
|
73
|
+
CloseButton: OverlayCloseButton,
|
|
74
|
+
Title: OverlayTitle,
|
|
75
|
+
Description: OverlayDescription,
|
|
76
|
+
Close: OverlayClose,
|
|
77
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
2
|
+
import type { ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
import type { PencilSurfaceVariant } from '../PencilSurface.js';
|
|
4
|
+
export type PopoverRootProps = PopoverPrimitive.PopoverProps;
|
|
5
|
+
export declare function PopoverRoot(props: PopoverRootProps): import("react").JSX.Element;
|
|
6
|
+
export type PopoverAnchorProps = PopoverPrimitive.PopoverAnchorProps;
|
|
7
|
+
export declare const PopoverAnchor: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverAnchorProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export type PopoverTriggerProps = PopoverPrimitive.PopoverTriggerProps;
|
|
9
|
+
export declare const PopoverTrigger: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
10
|
+
export type PopoverPortalProps = PopoverPrimitive.PopoverPortalProps;
|
|
11
|
+
export declare function PopoverPortal({ children, ...props }: PopoverPortalProps): import("react").JSX.Element;
|
|
12
|
+
export interface PopoverContentProps extends PopoverPrimitive.PopoverContentProps {
|
|
13
|
+
arrowProps?: PopoverArrowProps;
|
|
14
|
+
seed?: string | number;
|
|
15
|
+
showArrow?: boolean;
|
|
16
|
+
surfaceVariant?: PencilSurfaceVariant;
|
|
17
|
+
surfaceRadius?: number;
|
|
18
|
+
surfaceClassName?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const PopoverContent: import("react").ForwardRefExoticComponent<PopoverContentProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
21
|
+
export interface PopoverTitleProps extends Omit<ComponentPropsWithoutRef<typeof PopoverPrimitive.Title>, 'children'> {
|
|
22
|
+
children: string;
|
|
23
|
+
seed?: string | number;
|
|
24
|
+
}
|
|
25
|
+
export declare const PopoverTitle: import("react").ForwardRefExoticComponent<PopoverTitleProps & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
26
|
+
export interface PopoverDescriptionProps extends Omit<ComponentPropsWithoutRef<typeof PopoverPrimitive.Description>, 'children'> {
|
|
27
|
+
children: string;
|
|
28
|
+
seed?: string | number;
|
|
29
|
+
}
|
|
30
|
+
export declare const PopoverDescription: import("react").ForwardRefExoticComponent<PopoverDescriptionProps & import("react").RefAttributes<HTMLParagraphElement>>;
|
|
31
|
+
export type PopoverCloseProps = ComponentPropsWithoutRef<typeof PopoverPrimitive.Close>;
|
|
32
|
+
export declare const PopoverClose: import("react").ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverCloseProps & import("react").RefAttributes<HTMLButtonElement>, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
33
|
+
export type PopoverArrowProps = ComponentPropsWithoutRef<typeof PopoverPrimitive.Arrow>;
|
|
34
|
+
export declare const Popover: Readonly<{
|
|
35
|
+
Root: typeof PopoverRoot;
|
|
36
|
+
Anchor: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverAnchorProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
37
|
+
Trigger: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
38
|
+
Portal: typeof PopoverPortal;
|
|
39
|
+
Content: import("react").ForwardRefExoticComponent<PopoverContentProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
40
|
+
Title: import("react").ForwardRefExoticComponent<PopoverTitleProps & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
41
|
+
Description: import("react").ForwardRefExoticComponent<PopoverDescriptionProps & import("react").RefAttributes<HTMLParagraphElement>>;
|
|
42
|
+
Close: import("react").ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverCloseProps & import("react").RefAttributes<HTMLButtonElement>, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
43
|
+
}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
3
|
+
import { forwardRef, useId } from 'react';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
import { PencilText } from '../PencilText.js';
|
|
6
|
+
import { PortalThemeBridge, PortalThemeProvider, usePortalTriggerRef, } from './themeBridge.js';
|
|
7
|
+
function classes(...values) {
|
|
8
|
+
return values.filter(Boolean).join(' ');
|
|
9
|
+
}
|
|
10
|
+
export function PopoverRoot(props) {
|
|
11
|
+
return (_jsx(PortalThemeProvider, { children: _jsx(PopoverPrimitive.Root, { ...props }) }));
|
|
12
|
+
}
|
|
13
|
+
export const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
14
|
+
export const PopoverTrigger = forwardRef(function PopoverTrigger(props, forwardedRef) {
|
|
15
|
+
const ref = usePortalTriggerRef(forwardedRef);
|
|
16
|
+
return _jsx(PopoverPrimitive.Trigger, { ...props, ref: ref });
|
|
17
|
+
});
|
|
18
|
+
export function PopoverPortal({ children, ...props }) {
|
|
19
|
+
return (_jsx(PopoverPrimitive.Portal, { ...props, children: _jsx(PortalThemeBridge, { children: children }) }));
|
|
20
|
+
}
|
|
21
|
+
export const PopoverContent = forwardRef(function PopoverContent({ arrowProps, children, className, seed, showArrow = false, surfaceClassName, surfaceRadius = 8, surfaceVariant = 'paper', ...props }, ref) {
|
|
22
|
+
const id = useId();
|
|
23
|
+
return (_jsxs(PopoverPrimitive.Content, { ...props, className: classes('inkra-popover__content', className), ref: ref, children: [_jsx(PencilSurface, { className: classes('inkra-popover__surface', surfaceClassName), radius: surfaceRadius, seed: seed ?? `inkra-popover-${id}`, variant: surfaceVariant, children: _jsx("div", { className: "inkra-popover__content-body", children: children }) }), showArrow ? (_jsx(PopoverPrimitive.Arrow, { ...arrowProps, className: classes('inkra-popover__arrow', arrowProps?.className) })) : null] }));
|
|
24
|
+
});
|
|
25
|
+
export const PopoverTitle = forwardRef(function PopoverTitle({ children, className, seed, ...props }, ref) {
|
|
26
|
+
const id = useId();
|
|
27
|
+
return (_jsx(PopoverPrimitive.Title, { ...props, className: classes('inkra-popover__title', className), ref: ref, children: _jsx(PencilText, { seed: seed ?? `inkra-popover-title-${id}`, children: children }) }));
|
|
28
|
+
});
|
|
29
|
+
export const PopoverDescription = forwardRef(function PopoverDescription({ children, className, seed, ...props }, ref) {
|
|
30
|
+
const id = useId();
|
|
31
|
+
return (_jsx(PopoverPrimitive.Description, { ...props, className: classes('inkra-popover__description', className), ref: ref, children: _jsx(PencilText, { seed: seed ?? `inkra-popover-description-${id}`, children: children }) }));
|
|
32
|
+
});
|
|
33
|
+
export const PopoverClose = forwardRef(function PopoverClose(props, ref) {
|
|
34
|
+
return _jsx(PopoverPrimitive.Close, { ...props, ref: ref });
|
|
35
|
+
});
|
|
36
|
+
export const Popover = Object.freeze({
|
|
37
|
+
Root: PopoverRoot,
|
|
38
|
+
Anchor: PopoverAnchor,
|
|
39
|
+
Trigger: PopoverTrigger,
|
|
40
|
+
Portal: PopoverPortal,
|
|
41
|
+
Content: PopoverContent,
|
|
42
|
+
Title: PopoverTitle,
|
|
43
|
+
Description: PopoverDescription,
|
|
44
|
+
Close: PopoverClose,
|
|
45
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Overlay, OverlayBackdrop, OverlayClose, OverlayContent, OverlayHeader, OverlayBody, OverlayFooter, OverlayCloseButton, OverlayDescription, OverlayPortal, OverlayRoot, OverlayTitle, OverlayTrigger, } from './Overlay.js';
|
|
2
|
+
export type { OverlayBackdropProps, OverlayCloseProps, OverlayContentProps, OverlayHeaderProps, OverlayBodyProps, OverlayFooterProps, OverlayCloseButtonProps, OverlayDescriptionProps, OverlayPortalProps, OverlayRootProps, OverlayTitleProps, OverlayTriggerProps, } from './Overlay.js';
|
|
3
|
+
export { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverDescription, PopoverPortal, PopoverRoot, PopoverTitle, PopoverTrigger, } from './Popover.js';
|
|
4
|
+
export type { PopoverAnchorProps, PopoverArrowProps, PopoverCloseProps, PopoverContentProps, PopoverDescriptionProps, PopoverPortalProps, PopoverRootProps, PopoverTitleProps, PopoverTriggerProps, } from './Popover.js';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { Overlay, OverlayBackdrop, OverlayClose, OverlayContent, OverlayHeader, OverlayBody, OverlayFooter, OverlayCloseButton, OverlayDescription, OverlayPortal, OverlayRoot, OverlayTitle, OverlayTrigger, } from './Overlay.js';
|
|
2
|
+
export { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverDescription, PopoverPortal, PopoverRoot, PopoverTitle, PopoverTrigger, } from './Popover.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ForwardedRef, ReactNode } from 'react';
|
|
2
|
+
export declare function PortalThemeProvider({ children }: {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}): import("react").JSX.Element;
|
|
5
|
+
export declare function usePortalTriggerRef<Element extends HTMLElement>(forwardedRef: ForwardedRef<Element>): (node: Element | null) => void;
|
|
6
|
+
export declare const PortalThemeBridge: import("react").ForwardRefExoticComponent<{
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|