@neuralumina/lumina-ui 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,254 @@
1
+ import {
2
+ cleanStyle,
3
+ luminaTheme,
4
+ normalizeWidgetArgs,
5
+ omitProps,
6
+ px,
7
+ } from "./utils.js";
8
+
9
+ export function GestureDetector(
10
+ propsOrChildren = {},
11
+ maybeChildren = undefined,
12
+ ) {
13
+ const [props, children] = normalizeWidgetArgs(propsOrChildren, maybeChildren);
14
+ let longPressTimer = null;
15
+ const clearLongPress = () => {
16
+ if (longPressTimer) {
17
+ clearTimeout(longPressTimer);
18
+ longPressTimer = null;
19
+ }
20
+ };
21
+
22
+ return {
23
+ tag: "div",
24
+ props: {
25
+ ...omitProps(props, [
26
+ "onTap",
27
+ "onDoubleTap",
28
+ "onLongPress",
29
+ "onPanStart",
30
+ "onPanUpdate",
31
+ "onPanEnd",
32
+ "radius",
33
+ "longPressDelay",
34
+ ]),
35
+ onClick: props.onTap,
36
+ onDoubleClick: props.onDoubleTap,
37
+ onPointerDown: (event) => {
38
+ if (props.onPanStart) props.onPanStart(event);
39
+ if (props.onLongPress) {
40
+ clearLongPress();
41
+ longPressTimer = setTimeout(() => {
42
+ longPressTimer = null;
43
+ props.onLongPress(event);
44
+ }, props.longPressDelay ?? 500);
45
+ }
46
+ },
47
+ onPointerMove: (event) => {
48
+ clearLongPress();
49
+ if (props.onPanUpdate) props.onPanUpdate(event);
50
+ },
51
+ onPointerUp: (event) => {
52
+ clearLongPress();
53
+ if (props.onPanEnd) props.onPanEnd(event);
54
+ },
55
+ onPointerCancel: (event) => {
56
+ clearLongPress();
57
+ if (props.onPointerCancel) props.onPointerCancel(event);
58
+ },
59
+ style: cleanStyle({
60
+ touchAction: props.touchAction || "manipulation",
61
+ cursor: props.cursor || (props.onTap ? "pointer" : undefined),
62
+ borderRadius: px(props.radius),
63
+ ...props.style,
64
+ }),
65
+ },
66
+ children,
67
+ key: props.key,
68
+ };
69
+ }
70
+
71
+ export function AbsorbPointer(propsOrChildren = {}, maybeChildren = undefined) {
72
+ const [props, children] = normalizeWidgetArgs(propsOrChildren, maybeChildren);
73
+ const absorb = (event) => {
74
+ event.stopPropagation();
75
+ event.preventDefault();
76
+ };
77
+ const absorbing = props.absorbing !== false;
78
+ const stylePosition = props.style?.position;
79
+ const position =
80
+ absorbing && (!stylePosition || stylePosition === "static")
81
+ ? "relative"
82
+ : stylePosition;
83
+
84
+ return {
85
+ tag: "div",
86
+ props: {
87
+ ...omitProps(props, ["absorbing", "cursor", "dim"]),
88
+ style: cleanStyle({
89
+ opacity: absorbing && props.dim ? 0.62 : undefined,
90
+ ...props.style,
91
+ position,
92
+ }),
93
+ onClick: absorbing ? absorb : props.onClick,
94
+ onPointerDown: absorbing ? absorb : props.onPointerDown,
95
+ onPointerUp: absorbing ? absorb : props.onPointerUp,
96
+ onPointerMove: absorbing ? absorb : props.onPointerMove,
97
+ },
98
+ children: absorbing
99
+ ? [
100
+ ...children,
101
+ {
102
+ tag: "div",
103
+ props: {
104
+ "aria-hidden": "true",
105
+ onClick: absorb,
106
+ onPointerDown: absorb,
107
+ onPointerUp: absorb,
108
+ onPointerMove: absorb,
109
+ style: {
110
+ position: "absolute",
111
+ inset: 0,
112
+ zIndex: 2147483647,
113
+ backgroundColor: "transparent",
114
+ cursor: props.cursor,
115
+ },
116
+ },
117
+ children: [],
118
+ },
119
+ ]
120
+ : children,
121
+ key: props.key,
122
+ };
123
+ }
124
+
125
+ export function IgnorePointer(propsOrChildren = {}, maybeChildren = undefined) {
126
+ const [props, children] = normalizeWidgetArgs(propsOrChildren, maybeChildren);
127
+
128
+ return {
129
+ tag: "div",
130
+ props: {
131
+ ...omitProps(props, ["ignoring"]),
132
+ style: cleanStyle({
133
+ pointerEvents: props.ignoring === false ? undefined : "none",
134
+ ...props.style,
135
+ }),
136
+ },
137
+ children,
138
+ key: props.key,
139
+ };
140
+ }
141
+
142
+ export function Dismissible(propsOrChildren = {}, maybeChildren = undefined) {
143
+ const [props, children] = normalizeWidgetArgs(propsOrChildren, maybeChildren);
144
+ let startX = 0;
145
+ let startY = 0;
146
+ const threshold = props.threshold ?? 80;
147
+
148
+ return {
149
+ tag: "div",
150
+ props: {
151
+ ...omitProps(props, ["direction", "onDismissed", "threshold", "radius"]),
152
+ tabIndex: props.tabIndex ?? 0,
153
+ onClick: props.onClick,
154
+ onPointerDown: (event) => {
155
+ startX = event.clientX ?? 0;
156
+ startY = event.clientY ?? 0;
157
+ if (props.onPointerDown) props.onPointerDown(event);
158
+ },
159
+ onPointerUp: (event) => {
160
+ const dx = (event.clientX ?? 0) - startX;
161
+ const dy = (event.clientY ?? 0) - startY;
162
+ const horizontal = Math.abs(dx) >= threshold;
163
+ const vertical = Math.abs(dy) >= threshold;
164
+ const direction = props.direction || "horizontal";
165
+ const dismissed =
166
+ direction === "any" ||
167
+ (direction === "horizontal" && horizontal) ||
168
+ (direction === "vertical" && vertical) ||
169
+ (direction === "startToEnd" && dx >= threshold) ||
170
+ (direction === "endToStart" && dx <= -threshold);
171
+ if (dismissed && props.onDismissed) props.onDismissed(event);
172
+ if (props.onPointerUp) props.onPointerUp(event);
173
+ },
174
+ onKeyDown: (event) => {
175
+ if (event.key === "Delete" || event.key === "Backspace") {
176
+ if (props.onDismissed) props.onDismissed(event);
177
+ }
178
+ if (props.onKeyDown) props.onKeyDown(event);
179
+ },
180
+ style: cleanStyle({
181
+ touchAction: "pan-y",
182
+ borderRadius: px(props.radius),
183
+ transition: `opacity ${luminaTheme.transition}, transform ${luminaTheme.transition}, box-shadow ${luminaTheme.transition}`,
184
+ ...props.style,
185
+ }),
186
+ },
187
+ children,
188
+ key: props.key,
189
+ };
190
+ }
191
+
192
+ export function Draggable(propsOrChildren = {}, maybeChildren = undefined) {
193
+ const [props, children] = normalizeWidgetArgs(propsOrChildren, maybeChildren);
194
+
195
+ return {
196
+ tag: "div",
197
+ props: {
198
+ ...omitProps(props, ["data", "onDragStarted", "onDragCompleted", "radius"]),
199
+ draggable: true,
200
+ onDragStart: (event) => {
201
+ if (event.dataTransfer && props.data !== undefined) {
202
+ try {
203
+ event.dataTransfer.setData("text/plain", JSON.stringify(props.data));
204
+ } catch (error) {
205
+ event.dataTransfer.setData("text/plain", String(props.data));
206
+ }
207
+ }
208
+ if (props.onDragStarted) props.onDragStarted(event);
209
+ },
210
+ onDragEnd: props.onDragCompleted,
211
+ style: cleanStyle({
212
+ cursor: "grab",
213
+ width: px(props.width),
214
+ height: px(props.height),
215
+ borderRadius: px(props.radius),
216
+ transition: `box-shadow ${luminaTheme.transition}, transform ${luminaTheme.transition}`,
217
+ ...props.style,
218
+ }),
219
+ },
220
+ children,
221
+ key: props.key,
222
+ };
223
+ }
224
+
225
+ export function DragTarget(propsOrChildren = {}, maybeChildren = undefined) {
226
+ const [props, children] = normalizeWidgetArgs(propsOrChildren, maybeChildren);
227
+
228
+ return {
229
+ tag: "div",
230
+ props: {
231
+ ...omitProps(props, ["onAccept", "onWillAccept", "radius"]),
232
+ onDragOver: (event) => {
233
+ if (!props.onWillAccept || props.onWillAccept(event) !== false) {
234
+ event.preventDefault();
235
+ }
236
+ },
237
+ onDrop: (event) => {
238
+ event.preventDefault();
239
+ let data = event.dataTransfer?.getData("text/plain");
240
+ try {
241
+ data = data ? JSON.parse(data) : data;
242
+ } catch (e) {}
243
+ if (props.onAccept) props.onAccept(data, event);
244
+ },
245
+ style: cleanStyle({
246
+ borderRadius: px(props.radius),
247
+ transition: `background-color ${luminaTheme.transition}, border-color ${luminaTheme.transition}`,
248
+ ...props.style,
249
+ }),
250
+ },
251
+ children,
252
+ key: props.key,
253
+ };
254
+ }