@k2b/ui 0.19.0 → 0.21.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/dist/browser/actions/Button.js +20 -31
- package/dist/browser/actions/Button.js.map +1 -1
- package/dist/browser/actions/ContextMenu.js +7 -3
- package/dist/browser/actions/ContextMenu.js.map +1 -1
- package/dist/browser/actions/CopyButton.js +10 -8
- package/dist/browser/actions/CopyButton.js.map +1 -1
- package/dist/browser/actions/GestureMenu.js +471 -0
- package/dist/browser/actions/GestureMenu.js.map +1 -0
- package/dist/browser/actions/index.js +1 -0
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +3 -41
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/ChatPrimitives.js +9 -3
- package/dist/browser/chat/ChatPrimitives.js.map +1 -1
- package/dist/browser/chat/ChatTimeline.js +19 -1
- package/dist/browser/chat/ChatTimeline.js.map +1 -1
- package/dist/browser/content/Calendar.js +1951 -565
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/MessageRow.js +2 -2
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/content/calendar-messages.js +83 -0
- package/dist/browser/content/calendar-messages.js.map +1 -0
- package/dist/browser/content/calendar-month-layout.js +108 -0
- package/dist/browser/content/calendar-month-layout.js.map +1 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/inputs/DatePicker.js +46 -10
- package/dist/browser/inputs/DatePicker.js.map +1 -1
- package/dist/browser/inputs/TextInput.js +13 -1
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/inputs/date-picker.js +24 -0
- package/dist/browser/inputs/date-picker.js.map +1 -1
- package/dist/browser/inputs/markdown/actions.js +0 -42
- package/dist/browser/inputs/markdown/actions.js.map +1 -1
- package/dist/browser/intl/messages.js +6 -6
- package/dist/browser/intl/messages.js.map +1 -1
- package/dist/ssr/actions/Button.js +21 -32
- package/dist/ssr/actions/Button.js.map +1 -1
- package/dist/ssr/actions/ContextMenu.js +7 -3
- package/dist/ssr/actions/ContextMenu.js.map +1 -1
- package/dist/ssr/actions/CopyButton.js +9 -7
- package/dist/ssr/actions/CopyButton.js.map +1 -1
- package/dist/ssr/actions/GestureMenu.js +415 -0
- package/dist/ssr/actions/GestureMenu.js.map +1 -0
- package/dist/ssr/actions/index.js +1 -0
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +3 -41
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/ChatPrimitives.js +9 -3
- package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
- package/dist/ssr/chat/ChatTimeline.js +19 -1
- package/dist/ssr/chat/ChatTimeline.js.map +1 -1
- package/dist/ssr/content/Calendar.js +1223 -105
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +2 -2
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/content/calendar-messages.js +83 -0
- package/dist/ssr/content/calendar-messages.js.map +1 -0
- package/dist/ssr/content/calendar-month-layout.js +108 -0
- package/dist/ssr/content/calendar-month-layout.js.map +1 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/inputs/DatePicker.js +34 -4
- package/dist/ssr/inputs/DatePicker.js.map +1 -1
- package/dist/ssr/inputs/TextInput.js +13 -1
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/inputs/date-picker.js +24 -0
- package/dist/ssr/inputs/date-picker.js.map +1 -1
- package/dist/ssr/inputs/markdown/actions.js +0 -42
- package/dist/ssr/inputs/markdown/actions.js.map +1 -1
- package/dist/ssr/intl/messages.js +6 -6
- package/dist/ssr/intl/messages.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/GestureMenu.d.ts +37 -0
- package/dist/types/actions/index.d.ts +2 -0
- package/dist/types/chat/ChatComposer.d.ts +4 -1
- package/dist/types/content/Calendar.d.ts +32 -0
- package/dist/types/content/calendar-messages.d.ts +41 -0
- package/dist/types/content/calendar-month-layout.d.ts +52 -0
- package/dist/types/content/index.d.ts +1 -1
- package/dist/types/inputs/date-picker.d.ts +7 -0
- package/dist/types/inputs/markdown/actions.d.ts +0 -2
- package/dist/types/intl/messages.d.ts +3 -3
- package/package.json +2 -2
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { ssrStyleProperty as _$ssrStyleProperty } from "solid-js/web";
|
|
2
|
+
import { ssr as _$ssr } from "solid-js/web";
|
|
3
|
+
import { createComponent as _$createComponent } from "solid-js/web";
|
|
4
|
+
import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
|
|
5
|
+
import { escape as _$escape } from "solid-js/web";
|
|
6
|
+
var _tmpl$ = ["<div class=\"k2b-gesture-menu__sheet-menu\" role=\"menu\"", ">", "</div>"],
|
|
7
|
+
_tmpl$2 = ["<div class=\"k2b-gesture-menu\"", " style=\"", "\">", "<div class=\"k2b-gesture-menu__content\">", "</div></div>"],
|
|
8
|
+
_tmpl$3 = ["<span class=\"k2b-gesture-menu__swipe\" aria-hidden=\"true\"><i", "></i></span>"];
|
|
9
|
+
import { createSignal, onCleanup, onMount, Show } from "solid-js";
|
|
10
|
+
import { dialogCore } from "../feedback/dialog-core";
|
|
11
|
+
import { LocaleProvider, useLocale } from "../intl/locale";
|
|
12
|
+
import BottomSheet, { bottomSheetOptions } from "../layout/BottomSheet";
|
|
13
|
+
import { ContextMenu } from "./ContextMenu";
|
|
14
|
+
import { DropdownItems, focusRowSegment } from "./Dropdown";
|
|
15
|
+
|
|
16
|
+
/** A finger held this long without moving opens the menu, as the platforms' own long press does. */
|
|
17
|
+
const LONG_PRESS_MS = 500;
|
|
18
|
+
/** A second tap that starts this soon after the first ended makes a double tap. */
|
|
19
|
+
const DOUBLE_TAP_MS = 300;
|
|
20
|
+
/** The second tap lands at most this far from the first. */
|
|
21
|
+
const DOUBLE_TAP_DISTANCE = 32;
|
|
22
|
+
/** Movement up to this is still a tap or a long press. */
|
|
23
|
+
const SLOP = 10;
|
|
24
|
+
/** A swipe must be this many times wider than tall; anything steeper belongs to the page's scrolling. */
|
|
25
|
+
const SWIPE_RATIO = 1.5;
|
|
26
|
+
/** Travel at which a released swipe runs its action. */
|
|
27
|
+
const SWIPE_THRESHOLD = 64;
|
|
28
|
+
/** The content follows the finger up to the threshold, then at a quarter of its travel, up to this. */
|
|
29
|
+
const SWIPE_MAX = 96;
|
|
30
|
+
const SWIPE_RESISTANCE = 0.25;
|
|
31
|
+
/** Swipes do not start this close to the screen's side edges, where the system's back and forward gestures are. */
|
|
32
|
+
const EDGE = 20;
|
|
33
|
+
/** Duration of the snap back in `.k2b-gesture-menu[data-settling]`. */
|
|
34
|
+
const SETTLE_MS = 160;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gestures never start on these elements: links, controls, fields, code blocks, and anything marked
|
|
38
|
+
* `data-gesture-ignore` keep their own touch and mouse behavior.
|
|
39
|
+
*/
|
|
40
|
+
const IGNORE = ["a[href]", "button", "input", "textarea", "select", "summary", "label", "audio", "video", "pre", "[contenteditable]:not([contenteditable='false'])", "[role='button']", "[role='link']", "[role='checkbox']", "[role='switch']", "[role='slider']", "[role='tab']", "[role^='menuitem']", "[data-gesture-ignore]"].join(", ");
|
|
41
|
+
|
|
42
|
+
/** A menu action that a gesture runs as well. */
|
|
43
|
+
|
|
44
|
+
const gestureAction = (items, kind) => {
|
|
45
|
+
for (const item of items) for (const entry of "items" in item ? item.items : [item]) if ("gesture" in entry && entry.gesture === kind) return entry.disabled ? undefined : entry;
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
const swipeKind = direction => direction === "right" ? "swipe-right" : "swipe-left";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The sheet's items hand their action to `run` instead of running it while the sheet closes: `dialogCore` settles the
|
|
52
|
+
* sheet after it left the history, and only then may an action navigate or open the next dialog. A choice that keeps
|
|
53
|
+
* the menu open runs at once.
|
|
54
|
+
*/
|
|
55
|
+
const afterClose = (items, run) => {
|
|
56
|
+
const entry = item => {
|
|
57
|
+
const action = item.action;
|
|
58
|
+
if (!action || "choice" in item && item.closeOnSelect === false) return item;
|
|
59
|
+
return {
|
|
60
|
+
...item,
|
|
61
|
+
action: () => run(action)
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
return items.map(item => "items" in item ? {
|
|
65
|
+
...item,
|
|
66
|
+
items: item.items.map(entry)
|
|
67
|
+
} : entry(item));
|
|
68
|
+
};
|
|
69
|
+
const reducedMotion = () => typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
70
|
+
const menuItems = menu => Array.from(menu.querySelectorAll("[role^='menuitem']:not([aria-disabled='true'])"));
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The menu inside the long-press sheet: the same items, at touch size, with arrow keys for a connected keyboard. Focus
|
|
74
|
+
* starts on the sheet itself, which shows no ring on a phone; the first arrow key moves it into the menu.
|
|
75
|
+
*/
|
|
76
|
+
function SheetMenu(props) {
|
|
77
|
+
let menu;
|
|
78
|
+
const focus = index => {
|
|
79
|
+
const items = menuItems(menu);
|
|
80
|
+
items[(index + items.length) % items.length]?.focus();
|
|
81
|
+
};
|
|
82
|
+
const onKeyDown = event => {
|
|
83
|
+
const items = menuItems(menu);
|
|
84
|
+
const current = items.indexOf(document.activeElement);
|
|
85
|
+
if (event.key === "ArrowDown") focus(current + 1);else if (event.key === "ArrowUp") focus(current < 0 ? -1 : current - 1);else if (event.key === "Home" || event.key === "End") focus(event.key === "Home" ? 0 : -1);else if (!focusRowSegment(items, event.key)) return;
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
};
|
|
88
|
+
// Keys pressed on the sheet itself, while it is the top level of the shared dialog.
|
|
89
|
+
const onSheetKeyDown = event => {
|
|
90
|
+
if (event.target === props.dialog && props.dialog.lastElementChild?.contains(menu)) onKeyDown(event);
|
|
91
|
+
};
|
|
92
|
+
onMount(() => {
|
|
93
|
+
props.dialog.addEventListener("keydown", onSheetKeyDown);
|
|
94
|
+
onCleanup(() => props.dialog.removeEventListener("keydown", onSheetKeyDown));
|
|
95
|
+
});
|
|
96
|
+
return _$ssr(_tmpl$, _$ssrAttribute("aria-label", _$escape(props.label, true), false), _$escape(_$createComponent(DropdownItems, {
|
|
97
|
+
get items() {
|
|
98
|
+
return props.items;
|
|
99
|
+
},
|
|
100
|
+
close: () => props.close()
|
|
101
|
+
})));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Touch gestures for an element, each the shortcut of an entry in its menu: a horizontal swipe, a double tap, and a
|
|
106
|
+
* long press that opens the whole menu as a `BottomSheet`. A mouse double-click runs the double-tap action; a
|
|
107
|
+
* right-click and the keyboard open the menu beside the element. Vertical scrolling never turns into a swipe.
|
|
108
|
+
*/
|
|
109
|
+
export function GestureMenu(props) {
|
|
110
|
+
const locale = useLocale();
|
|
111
|
+
const [swipe, setSwipe] = createSignal();
|
|
112
|
+
let surface;
|
|
113
|
+
let press;
|
|
114
|
+
let lastTap;
|
|
115
|
+
let lastPointer = "mouse";
|
|
116
|
+
/**
|
|
117
|
+
* The touch or pen press that just ended did something: its `touchend` is cancelled, so no click or mouse events
|
|
118
|
+
* follow, and a pen's click, which comes without touch events, is cancelled itself.
|
|
119
|
+
*/
|
|
120
|
+
let consumed = false;
|
|
121
|
+
let sheetOpen = false;
|
|
122
|
+
/** Closes an open sheet when the element goes, for example when its message is deleted. */
|
|
123
|
+
const disposed = new AbortController();
|
|
124
|
+
let reduced = false;
|
|
125
|
+
let settleTimer;
|
|
126
|
+
const active = () => props.items.length > 0;
|
|
127
|
+
const action = kind => gestureAction(props.items, kind);
|
|
128
|
+
const touchAction = () => action("swipe-right") || action("swipe-left") ? "swipe" : action("double-tap") ? "tap" : undefined;
|
|
129
|
+
|
|
130
|
+
/** Whether the event started on something with a meaning of its own, inside this element. */
|
|
131
|
+
const ignored = target => {
|
|
132
|
+
const found = target instanceof Element ? target.closest(IGNORE) : null;
|
|
133
|
+
return found !== null && found !== surface && surface.contains(found);
|
|
134
|
+
};
|
|
135
|
+
const openSheet = () => {
|
|
136
|
+
if (sheetOpen || !active()) return;
|
|
137
|
+
sheetOpen = true;
|
|
138
|
+
props.onOpen?.();
|
|
139
|
+
const current = locale();
|
|
140
|
+
let chosen;
|
|
141
|
+
const choose = action => {
|
|
142
|
+
chosen = action;
|
|
143
|
+
};
|
|
144
|
+
void dialogCore.open((close, context) => _$createComponent(LocaleProvider, {
|
|
145
|
+
locale: current,
|
|
146
|
+
get children() {
|
|
147
|
+
return _$createComponent(BottomSheet, {
|
|
148
|
+
get onDismiss() {
|
|
149
|
+
return context.requestDismiss;
|
|
150
|
+
},
|
|
151
|
+
get children() {
|
|
152
|
+
return _$createComponent(BottomSheet.Body, {
|
|
153
|
+
get children() {
|
|
154
|
+
return [props.sheetTop?.(() => close()), _$createComponent(SheetMenu, {
|
|
155
|
+
get items() {
|
|
156
|
+
return afterClose(props.items, choose);
|
|
157
|
+
},
|
|
158
|
+
get label() {
|
|
159
|
+
return props.label;
|
|
160
|
+
},
|
|
161
|
+
close: () => close(),
|
|
162
|
+
get dialog() {
|
|
163
|
+
return context.dialog;
|
|
164
|
+
}
|
|
165
|
+
})];
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}), {
|
|
172
|
+
...bottomSheetOptions,
|
|
173
|
+
ariaLabel: props.label,
|
|
174
|
+
history: true,
|
|
175
|
+
signal: disposed.signal
|
|
176
|
+
}).then(() => chosen?.()).finally(() => {
|
|
177
|
+
sheetOpen = false;
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/** Another pointer that goes down anywhere while a finger presses, as in a pinch, ends the gesture. */
|
|
182
|
+
const onOtherPointer = event => {
|
|
183
|
+
if (!press || event.pointerId === press.id) return;
|
|
184
|
+
if (press.direction) settle();
|
|
185
|
+
endPress();
|
|
186
|
+
};
|
|
187
|
+
const endPress = () => {
|
|
188
|
+
clearTimeout(press?.timer);
|
|
189
|
+
press = undefined;
|
|
190
|
+
document.removeEventListener("pointerdown", onOtherPointer, true);
|
|
191
|
+
delete surface.dataset.pressing;
|
|
192
|
+
};
|
|
193
|
+
const settle = () => {
|
|
194
|
+
const current = swipe();
|
|
195
|
+
if (!current) return;
|
|
196
|
+
clearTimeout(settleTimer);
|
|
197
|
+
if (reduced || current.offset === 0) {
|
|
198
|
+
setSwipe(undefined);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
setSwipe({
|
|
202
|
+
...current,
|
|
203
|
+
offset: 0,
|
|
204
|
+
progress: 0,
|
|
205
|
+
armed: false,
|
|
206
|
+
settling: true
|
|
207
|
+
});
|
|
208
|
+
settleTimer = setTimeout(() => setSwipe(undefined), SETTLE_MS);
|
|
209
|
+
};
|
|
210
|
+
const moveSwipe = (current, direction, dx) => {
|
|
211
|
+
const travel = Math.max(0, direction === "right" ? dx : -dx);
|
|
212
|
+
const moved = travel <= SWIPE_THRESHOLD ? travel : SWIPE_THRESHOLD + (travel - SWIPE_THRESHOLD) * SWIPE_RESISTANCE;
|
|
213
|
+
const offset = reduced ? 0 : Math.min(SWIPE_MAX, moved) * (direction === "right" ? 1 : -1);
|
|
214
|
+
current.direction = direction;
|
|
215
|
+
setSwipe({
|
|
216
|
+
direction,
|
|
217
|
+
icon: action(swipeKind(direction))?.icon,
|
|
218
|
+
offset,
|
|
219
|
+
progress: Math.min(1, travel / SWIPE_THRESHOLD),
|
|
220
|
+
armed: travel >= SWIPE_THRESHOLD,
|
|
221
|
+
settling: false
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
const longPress = () => {
|
|
225
|
+
if (!press) return;
|
|
226
|
+
press.long = true;
|
|
227
|
+
lastTap = undefined;
|
|
228
|
+
openSheet();
|
|
229
|
+
};
|
|
230
|
+
const onPointerDown = event => {
|
|
231
|
+
lastPointer = event.pointerType;
|
|
232
|
+
consumed = false;
|
|
233
|
+
// Any press in between, on a link, with a mouse, or with another finger, ends a pending double tap.
|
|
234
|
+
const previous = lastTap;
|
|
235
|
+
lastTap = undefined;
|
|
236
|
+
// Another finger ends the gesture through `onOtherPointer` and starts none.
|
|
237
|
+
if (event.pointerType === "mouse" || !event.isPrimary) return;
|
|
238
|
+
endPress();
|
|
239
|
+
if (!active() || ignored(event.target)) return;
|
|
240
|
+
const second = previous !== undefined && performance.now() - previous.at <= DOUBLE_TAP_MS && Math.hypot(event.clientX - previous.x, event.clientY - previous.y) <= DOUBLE_TAP_DISTANCE;
|
|
241
|
+
press = {
|
|
242
|
+
id: event.pointerId,
|
|
243
|
+
x: event.clientX,
|
|
244
|
+
y: event.clientY,
|
|
245
|
+
moved: false,
|
|
246
|
+
long: false,
|
|
247
|
+
second,
|
|
248
|
+
edge: event.clientX < EDGE || event.clientX > window.innerWidth - EDGE,
|
|
249
|
+
timer: setTimeout(longPress, LONG_PRESS_MS)
|
|
250
|
+
};
|
|
251
|
+
document.addEventListener("pointerdown", onOtherPointer, true);
|
|
252
|
+
// Hybrid devices keep text selection for the mouse; a finger on the element selects nothing.
|
|
253
|
+
surface.dataset.pressing = "";
|
|
254
|
+
};
|
|
255
|
+
const onPointerMove = event => {
|
|
256
|
+
const current = press;
|
|
257
|
+
if (!current || event.pointerId !== current.id || current.long) return;
|
|
258
|
+
const dx = event.clientX - current.x;
|
|
259
|
+
const dy = event.clientY - current.y;
|
|
260
|
+
if (current.direction) {
|
|
261
|
+
moveSwipe(current, current.direction, dx);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (current.moved || Math.hypot(dx, dy) < SLOP) return;
|
|
265
|
+
current.moved = true;
|
|
266
|
+
clearTimeout(current.timer);
|
|
267
|
+
if (current.edge || Math.abs(dx) < Math.abs(dy) * SWIPE_RATIO) return;
|
|
268
|
+
const direction = dx > 0 ? "right" : "left";
|
|
269
|
+
if (!action(swipeKind(direction))) return;
|
|
270
|
+
clearTimeout(settleTimer);
|
|
271
|
+
reduced = reducedMotion();
|
|
272
|
+
moveSwipe(current, direction, dx);
|
|
273
|
+
};
|
|
274
|
+
const onPointerUp = event => {
|
|
275
|
+
const current = press;
|
|
276
|
+
if (!current || event.pointerId !== current.id) return;
|
|
277
|
+
endPress();
|
|
278
|
+
if (current.long) {
|
|
279
|
+
consumed = true;
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (current.direction) {
|
|
283
|
+
// Where the finger lifts decides, and the item as it is now.
|
|
284
|
+
moveSwipe(current, current.direction, event.clientX - current.x);
|
|
285
|
+
const swipeAction = swipe()?.armed ? action(swipeKind(current.direction)) : undefined;
|
|
286
|
+
settle();
|
|
287
|
+
if (swipeAction) {
|
|
288
|
+
consumed = true;
|
|
289
|
+
swipeAction.action();
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (current.moved) return;
|
|
294
|
+
const doubleTap = action("double-tap");
|
|
295
|
+
if (!doubleTap) return;
|
|
296
|
+
if (current.second) {
|
|
297
|
+
consumed = true;
|
|
298
|
+
doubleTap.action();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
lastTap = {
|
|
302
|
+
x: event.clientX,
|
|
303
|
+
y: event.clientY,
|
|
304
|
+
at: performance.now()
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// The browser took the touch, for example to scroll the page.
|
|
309
|
+
const onPointerCancel = event => {
|
|
310
|
+
if (!press || event.pointerId !== press.id) return;
|
|
311
|
+
if (press.direction) settle();
|
|
312
|
+
endPress();
|
|
313
|
+
lastTap = undefined;
|
|
314
|
+
};
|
|
315
|
+
const onTouchEnd = event => {
|
|
316
|
+
if (!consumed) return;
|
|
317
|
+
consumed = false;
|
|
318
|
+
if (event.cancelable) event.preventDefault();
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// A pen sends no touch events; the click after its gesture is cancelled here. A keyboard's click has no detail.
|
|
322
|
+
const onClick = event => {
|
|
323
|
+
if (!consumed || event.detail === 0) return;
|
|
324
|
+
consumed = false;
|
|
325
|
+
event.preventDefault();
|
|
326
|
+
event.stopPropagation();
|
|
327
|
+
};
|
|
328
|
+
const onContextMenu = event => {
|
|
329
|
+
// Without items the element has no menu, and the browser keeps its own.
|
|
330
|
+
if (!active()) return;
|
|
331
|
+
// Links, fields, and code keep the browser's own menu.
|
|
332
|
+
if (ignored(event.target)) {
|
|
333
|
+
event.stopPropagation();
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// A mouse or the keyboard opens the menu beside the element through ContextMenu.
|
|
337
|
+
if (!press && !("pointerType" in event && (event.pointerType === "touch" || event.pointerType === "pen"))) return;
|
|
338
|
+
// Android and touch screens report a long press as a context-menu event, sometimes before the timer.
|
|
339
|
+
event.preventDefault();
|
|
340
|
+
event.stopPropagation();
|
|
341
|
+
if (press && !press.long && !press.moved) {
|
|
342
|
+
clearTimeout(press.timer);
|
|
343
|
+
longPress();
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// The second press of a mouse double-click would select a word; in this element it runs the double-tap action.
|
|
348
|
+
const onMouseDown = event => {
|
|
349
|
+
if (event.button === 0 && event.detail >= 2 && lastPointer === "mouse" && action("double-tap") && !ignored(event.target)) event.preventDefault();
|
|
350
|
+
};
|
|
351
|
+
const onDoubleClick = event => {
|
|
352
|
+
if (lastPointer !== "mouse" || ignored(event.target)) return;
|
|
353
|
+
const doubleTap = action("double-tap");
|
|
354
|
+
if (!doubleTap) return;
|
|
355
|
+
event.preventDefault();
|
|
356
|
+
doubleTap.action();
|
|
357
|
+
};
|
|
358
|
+
onMount(() => {
|
|
359
|
+
surface.addEventListener("pointerdown", onPointerDown);
|
|
360
|
+
surface.addEventListener("pointermove", onPointerMove);
|
|
361
|
+
surface.addEventListener("pointerup", onPointerUp);
|
|
362
|
+
surface.addEventListener("pointercancel", onPointerCancel);
|
|
363
|
+
surface.addEventListener("touchend", onTouchEnd, {
|
|
364
|
+
passive: false
|
|
365
|
+
});
|
|
366
|
+
surface.addEventListener("click", onClick, true);
|
|
367
|
+
surface.addEventListener("contextmenu", onContextMenu);
|
|
368
|
+
surface.addEventListener("mousedown", onMouseDown);
|
|
369
|
+
surface.addEventListener("dblclick", onDoubleClick);
|
|
370
|
+
onCleanup(() => {
|
|
371
|
+
disposed.abort();
|
|
372
|
+
endPress();
|
|
373
|
+
clearTimeout(settleTimer);
|
|
374
|
+
surface.removeEventListener("pointerdown", onPointerDown);
|
|
375
|
+
surface.removeEventListener("pointermove", onPointerMove);
|
|
376
|
+
surface.removeEventListener("pointerup", onPointerUp);
|
|
377
|
+
surface.removeEventListener("pointercancel", onPointerCancel);
|
|
378
|
+
surface.removeEventListener("touchend", onTouchEnd);
|
|
379
|
+
surface.removeEventListener("click", onClick, true);
|
|
380
|
+
surface.removeEventListener("contextmenu", onContextMenu);
|
|
381
|
+
surface.removeEventListener("mousedown", onMouseDown);
|
|
382
|
+
surface.removeEventListener("dblclick", onDoubleClick);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
return _$createComponent(ContextMenu, {
|
|
386
|
+
get items() {
|
|
387
|
+
return props.items;
|
|
388
|
+
},
|
|
389
|
+
get label() {
|
|
390
|
+
return props.label;
|
|
391
|
+
},
|
|
392
|
+
get tabIndex() {
|
|
393
|
+
return props.tabIndex;
|
|
394
|
+
},
|
|
395
|
+
get disabled() {
|
|
396
|
+
return !active();
|
|
397
|
+
},
|
|
398
|
+
get onOpen() {
|
|
399
|
+
return props.onOpen;
|
|
400
|
+
},
|
|
401
|
+
get ["class"]() {
|
|
402
|
+
return props.class;
|
|
403
|
+
},
|
|
404
|
+
get children() {
|
|
405
|
+
return _$ssr(_tmpl$2, _$ssrAttribute("data-active", active() ? "" : _$escape(undefined, true), false) + _$ssrAttribute("data-touch", _$escape(touchAction(), true), false) + _$ssrAttribute("data-swipe", _$escape(swipe()?.direction, true), false) + _$ssrAttribute("data-armed", swipe()?.armed ? "" : _$escape(undefined, true), false) + _$ssrAttribute("data-settling", swipe()?.settling ? "" : _$escape(undefined, true), false), _$ssrStyleProperty("--k2b-gesture-menu-offset:", `${_$escape(swipe()?.offset ?? 0, true)}px`) + _$ssrStyleProperty(";--k2b-gesture-menu-progress:", _$escape(String(swipe()?.progress ?? 0), true)), _$escape(_$createComponent(Show, {
|
|
406
|
+
get when() {
|
|
407
|
+
return swipe();
|
|
408
|
+
},
|
|
409
|
+
children: current => _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(current().icon ?? (current().direction === "right" ? "ti ti-arrow-right" : "ti ti-arrow-left"), true), false))
|
|
410
|
+
})), _$escape(props.children));
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
export default GestureMenu;
|
|
415
|
+
//# sourceMappingURL=GestureMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["actions/GestureMenu.tsx"],"sourcesContent":["import { createSignal, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { dialogCore } from \"../feedback/dialog-core\";\nimport { LocaleProvider, useLocale } from \"../intl/locale\";\nimport BottomSheet, { bottomSheetOptions } from \"../layout/BottomSheet\";\nimport { ContextMenu } from \"./ContextMenu\";\nimport {\n type DropdownAction,\n type DropdownActionBase,\n type DropdownChoice,\n DropdownItems,\n type DropdownSection,\n focusRowSegment,\n} from \"./Dropdown\";\n\n/** A finger held this long without moving opens the menu, as the platforms' own long press does. */\nconst LONG_PRESS_MS = 500;\n/** A second tap that starts this soon after the first ended makes a double tap. */\nconst DOUBLE_TAP_MS = 300;\n/** The second tap lands at most this far from the first. */\nconst DOUBLE_TAP_DISTANCE = 32;\n/** Movement up to this is still a tap or a long press. */\nconst SLOP = 10;\n/** A swipe must be this many times wider than tall; anything steeper belongs to the page's scrolling. */\nconst SWIPE_RATIO = 1.5;\n/** Travel at which a released swipe runs its action. */\nconst SWIPE_THRESHOLD = 64;\n/** The content follows the finger up to the threshold, then at a quarter of its travel, up to this. */\nconst SWIPE_MAX = 96;\nconst SWIPE_RESISTANCE = 0.25;\n/** Swipes do not start this close to the screen's side edges, where the system's back and forward gestures are. */\nconst EDGE = 20;\n/** Duration of the snap back in `.k2b-gesture-menu[data-settling]`. */\nconst SETTLE_MS = 160;\n\n/**\n * Gestures never start on these elements: links, controls, fields, code blocks, and anything marked\n * `data-gesture-ignore` keep their own touch and mouse behavior.\n */\nconst IGNORE = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"textarea\",\n \"select\",\n \"summary\",\n \"label\",\n \"audio\",\n \"video\",\n \"pre\",\n \"[contenteditable]:not([contenteditable='false'])\",\n \"[role='button']\",\n \"[role='link']\",\n \"[role='checkbox']\",\n \"[role='switch']\",\n \"[role='slider']\",\n \"[role='tab']\",\n \"[role^='menuitem']\",\n \"[data-gesture-ignore]\",\n].join(\", \");\n\nexport type GestureKind = \"swipe-right\" | \"swipe-left\" | \"double-tap\";\n\n/** A menu action that a gesture runs as well. */\nexport type GestureMenuAction = DropdownActionBase & {\n action: () => void;\n /** The gesture that runs this action. Give each gesture to at most one item; a disabled item disables its gesture. */\n gesture?: GestureKind;\n};\n\nexport type GestureMenuSection = Omit<DropdownSection, \"items\"> & {\n items: readonly (GestureMenuAction | DropdownAction | DropdownChoice)[];\n};\n\nexport type GestureMenuItem = GestureMenuAction | DropdownAction | DropdownChoice | GestureMenuSection;\n\nexport type GestureMenuProps = {\n /**\n * The menu. A long press opens it as a bottom sheet, a right-click, the Context Menu key, or Shift+F10 as a menu.\n * An item with `gesture` is also run by that gesture, so every gesture has its entry here.\n */\n items: readonly GestureMenuItem[];\n /** Names the element and its menu, for example \"Message from Nora, 10:42\". */\n label: string;\n children: JSX.Element;\n /** Shown above the menu in the long-press sheet, for example a row of quick reactions. `close` closes the sheet. */\n sheetTop?: (close: () => void) => JSX.Element;\n /** Tab order of the element. A list that moves focus between its rows itself passes -1. */\n tabIndex?: number;\n /** Runs before the menu or the sheet shows its items, for example to select what was pressed. */\n onOpen?: () => void;\n class?: string;\n};\n\ntype SwipeDirection = \"right\" | \"left\";\n\ntype Swipe = {\n direction: SwipeDirection;\n icon?: string;\n /** How far the content is moved, in pixels; always 0 with reduced motion. */\n offset: number;\n /** Travel relative to the threshold, from 0 to 1. */\n progress: number;\n armed: boolean;\n settling: boolean;\n};\n\ntype Press = {\n id: number;\n x: number;\n y: number;\n timer?: ReturnType<typeof setTimeout>;\n /** The finger left the slop: no tap or long press any more. */\n moved: boolean;\n /** The long press opened the sheet. */\n long: boolean;\n /** The press started soon after a tap nearby. */\n second: boolean;\n /** The press started at a side edge of the screen. */\n edge: boolean;\n direction?: SwipeDirection;\n};\n\ntype Entry = GestureMenuAction | DropdownAction | DropdownChoice;\n\nconst gestureAction = (items: readonly GestureMenuItem[], kind: GestureKind): GestureMenuAction | undefined => {\n for (const item of items)\n for (const entry of \"items\" in item ? item.items : [item])\n if (\"gesture\" in entry && entry.gesture === kind) return entry.disabled ? undefined : entry;\n return undefined;\n};\n\nconst swipeKind = (direction: SwipeDirection): GestureKind => (direction === \"right\" ? \"swipe-right\" : \"swipe-left\");\n\n/**\n * The sheet's items hand their action to `run` instead of running it while the sheet closes: `dialogCore` settles the\n * sheet after it left the history, and only then may an action navigate or open the next dialog. A choice that keeps\n * the menu open runs at once.\n */\nconst afterClose = (items: readonly GestureMenuItem[], run: (action: () => void) => void): GestureMenuItem[] => {\n const entry = (item: Entry): Entry => {\n const action = item.action;\n if (!action || (\"choice\" in item && item.closeOnSelect === false)) return item;\n return { ...item, action: () => run(action) };\n };\n return items.map((item) => (\"items\" in item ? { ...item, items: item.items.map(entry) } : entry(item)));\n};\n\nconst reducedMotion = () => typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\nconst menuItems = (menu: HTMLElement): HTMLElement[] =>\n Array.from(menu.querySelectorAll<HTMLElement>(\"[role^='menuitem']:not([aria-disabled='true'])\"));\n\n/**\n * The menu inside the long-press sheet: the same items, at touch size, with arrow keys for a connected keyboard. Focus\n * starts on the sheet itself, which shows no ring on a phone; the first arrow key moves it into the menu.\n */\nfunction SheetMenu(props: { items: readonly GestureMenuItem[]; label: string; close: () => void; dialog: HTMLDialogElement }): JSX.Element {\n let menu!: HTMLDivElement;\n const focus = (index: number) => {\n const items = menuItems(menu);\n items[(index + items.length) % items.length]?.focus();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n const items = menuItems(menu);\n const current = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === \"ArrowDown\") focus(current + 1);\n else if (event.key === \"ArrowUp\") focus(current < 0 ? -1 : current - 1);\n else if (event.key === \"Home\" || event.key === \"End\") focus(event.key === \"Home\" ? 0 : -1);\n else if (!focusRowSegment(items, event.key)) return;\n event.preventDefault();\n };\n // Keys pressed on the sheet itself, while it is the top level of the shared dialog.\n const onSheetKeyDown = (event: KeyboardEvent) => {\n if (event.target === props.dialog && props.dialog.lastElementChild?.contains(menu)) onKeyDown(event);\n };\n onMount(() => {\n props.dialog.addEventListener(\"keydown\", onSheetKeyDown);\n onCleanup(() => props.dialog.removeEventListener(\"keydown\", onSheetKeyDown));\n });\n return (\n <div ref={menu} class=\"k2b-gesture-menu__sheet-menu\" role=\"menu\" aria-label={props.label} onKeyDown={onKeyDown}>\n <DropdownItems items={props.items} close={() => props.close()} />\n </div>\n );\n}\n\n/**\n * Touch gestures for an element, each the shortcut of an entry in its menu: a horizontal swipe, a double tap, and a\n * long press that opens the whole menu as a `BottomSheet`. A mouse double-click runs the double-tap action; a\n * right-click and the keyboard open the menu beside the element. Vertical scrolling never turns into a swipe.\n */\nexport function GestureMenu(props: GestureMenuProps): JSX.Element {\n const locale = useLocale();\n const [swipe, setSwipe] = createSignal<Swipe>();\n let surface!: HTMLDivElement;\n let press: Press | undefined;\n let lastTap: { x: number; y: number; at: number } | undefined;\n let lastPointer = \"mouse\";\n /**\n * The touch or pen press that just ended did something: its `touchend` is cancelled, so no click or mouse events\n * follow, and a pen's click, which comes without touch events, is cancelled itself.\n */\n let consumed = false;\n let sheetOpen = false;\n /** Closes an open sheet when the element goes, for example when its message is deleted. */\n const disposed = new AbortController();\n let reduced = false;\n let settleTimer: ReturnType<typeof setTimeout> | undefined;\n\n const active = () => props.items.length > 0;\n const action = (kind: GestureKind) => gestureAction(props.items, kind);\n const touchAction = () => (action(\"swipe-right\") || action(\"swipe-left\") ? \"swipe\" : action(\"double-tap\") ? \"tap\" : undefined);\n\n /** Whether the event started on something with a meaning of its own, inside this element. */\n const ignored = (target: EventTarget | null) => {\n const found = target instanceof Element ? target.closest(IGNORE) : null;\n return found !== null && found !== surface && surface.contains(found);\n };\n\n const openSheet = () => {\n if (sheetOpen || !active()) return;\n sheetOpen = true;\n props.onOpen?.();\n const current = locale();\n let chosen: (() => void) | undefined;\n const choose = (action: () => void) => {\n chosen = action;\n };\n void dialogCore\n .open<void>(\n (close, context) => (\n <LocaleProvider locale={current}>\n <BottomSheet onDismiss={context.requestDismiss}>\n <BottomSheet.Body>\n {props.sheetTop?.(() => close())}\n <SheetMenu items={afterClose(props.items, choose)} label={props.label} close={() => close()} dialog={context.dialog} />\n </BottomSheet.Body>\n </BottomSheet>\n </LocaleProvider>\n ),\n { ...bottomSheetOptions, ariaLabel: props.label, history: true, signal: disposed.signal },\n )\n .then(() => chosen?.())\n .finally(() => {\n sheetOpen = false;\n });\n };\n\n /** Another pointer that goes down anywhere while a finger presses, as in a pinch, ends the gesture. */\n const onOtherPointer = (event: PointerEvent) => {\n if (!press || event.pointerId === press.id) return;\n if (press.direction) settle();\n endPress();\n };\n\n const endPress = () => {\n clearTimeout(press?.timer);\n press = undefined;\n document.removeEventListener(\"pointerdown\", onOtherPointer, true);\n delete surface.dataset.pressing;\n };\n\n const settle = () => {\n const current = swipe();\n if (!current) return;\n clearTimeout(settleTimer);\n if (reduced || current.offset === 0) {\n setSwipe(undefined);\n return;\n }\n setSwipe({ ...current, offset: 0, progress: 0, armed: false, settling: true });\n settleTimer = setTimeout(() => setSwipe(undefined), SETTLE_MS);\n };\n\n const moveSwipe = (current: Press, direction: SwipeDirection, dx: number) => {\n const travel = Math.max(0, direction === \"right\" ? dx : -dx);\n const moved = travel <= SWIPE_THRESHOLD ? travel : SWIPE_THRESHOLD + (travel - SWIPE_THRESHOLD) * SWIPE_RESISTANCE;\n const offset = reduced ? 0 : Math.min(SWIPE_MAX, moved) * (direction === \"right\" ? 1 : -1);\n current.direction = direction;\n setSwipe({\n direction,\n icon: action(swipeKind(direction))?.icon,\n offset,\n progress: Math.min(1, travel / SWIPE_THRESHOLD),\n armed: travel >= SWIPE_THRESHOLD,\n settling: false,\n });\n };\n\n const longPress = () => {\n if (!press) return;\n press.long = true;\n lastTap = undefined;\n openSheet();\n };\n\n const onPointerDown = (event: PointerEvent) => {\n lastPointer = event.pointerType;\n consumed = false;\n // Any press in between, on a link, with a mouse, or with another finger, ends a pending double tap.\n const previous = lastTap;\n lastTap = undefined;\n // Another finger ends the gesture through `onOtherPointer` and starts none.\n if (event.pointerType === \"mouse\" || !event.isPrimary) return;\n endPress();\n if (!active() || ignored(event.target)) return;\n const second =\n previous !== undefined &&\n performance.now() - previous.at <= DOUBLE_TAP_MS &&\n Math.hypot(event.clientX - previous.x, event.clientY - previous.y) <= DOUBLE_TAP_DISTANCE;\n press = {\n id: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n moved: false,\n long: false,\n second,\n edge: event.clientX < EDGE || event.clientX > window.innerWidth - EDGE,\n timer: setTimeout(longPress, LONG_PRESS_MS),\n };\n document.addEventListener(\"pointerdown\", onOtherPointer, true);\n // Hybrid devices keep text selection for the mouse; a finger on the element selects nothing.\n surface.dataset.pressing = \"\";\n };\n\n const onPointerMove = (event: PointerEvent) => {\n const current = press;\n if (!current || event.pointerId !== current.id || current.long) return;\n const dx = event.clientX - current.x;\n const dy = event.clientY - current.y;\n if (current.direction) {\n moveSwipe(current, current.direction, dx);\n return;\n }\n if (current.moved || Math.hypot(dx, dy) < SLOP) return;\n current.moved = true;\n clearTimeout(current.timer);\n if (current.edge || Math.abs(dx) < Math.abs(dy) * SWIPE_RATIO) return;\n const direction: SwipeDirection = dx > 0 ? \"right\" : \"left\";\n if (!action(swipeKind(direction))) return;\n clearTimeout(settleTimer);\n reduced = reducedMotion();\n moveSwipe(current, direction, dx);\n };\n\n const onPointerUp = (event: PointerEvent) => {\n const current = press;\n if (!current || event.pointerId !== current.id) return;\n endPress();\n if (current.long) {\n consumed = true;\n return;\n }\n if (current.direction) {\n // Where the finger lifts decides, and the item as it is now.\n moveSwipe(current, current.direction, event.clientX - current.x);\n const swipeAction = swipe()?.armed ? action(swipeKind(current.direction)) : undefined;\n settle();\n if (swipeAction) {\n consumed = true;\n swipeAction.action();\n }\n return;\n }\n if (current.moved) return;\n const doubleTap = action(\"double-tap\");\n if (!doubleTap) return;\n if (current.second) {\n consumed = true;\n doubleTap.action();\n return;\n }\n lastTap = { x: event.clientX, y: event.clientY, at: performance.now() };\n };\n\n // The browser took the touch, for example to scroll the page.\n const onPointerCancel = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (press.direction) settle();\n endPress();\n lastTap = undefined;\n };\n\n const onTouchEnd = (event: TouchEvent) => {\n if (!consumed) return;\n consumed = false;\n if (event.cancelable) event.preventDefault();\n };\n\n // A pen sends no touch events; the click after its gesture is cancelled here. A keyboard's click has no detail.\n const onClick = (event: MouseEvent) => {\n if (!consumed || event.detail === 0) return;\n consumed = false;\n event.preventDefault();\n event.stopPropagation();\n };\n\n const onContextMenu = (event: MouseEvent) => {\n // Without items the element has no menu, and the browser keeps its own.\n if (!active()) return;\n // Links, fields, and code keep the browser's own menu.\n if (ignored(event.target)) {\n event.stopPropagation();\n return;\n }\n // A mouse or the keyboard opens the menu beside the element through ContextMenu.\n if (!press && !(\"pointerType\" in event && (event.pointerType === \"touch\" || event.pointerType === \"pen\"))) return;\n // Android and touch screens report a long press as a context-menu event, sometimes before the timer.\n event.preventDefault();\n event.stopPropagation();\n if (press && !press.long && !press.moved) {\n clearTimeout(press.timer);\n longPress();\n }\n };\n\n // The second press of a mouse double-click would select a word; in this element it runs the double-tap action.\n const onMouseDown = (event: MouseEvent) => {\n if (event.button === 0 && event.detail >= 2 && lastPointer === \"mouse\" && action(\"double-tap\") && !ignored(event.target))\n event.preventDefault();\n };\n\n const onDoubleClick = (event: MouseEvent) => {\n if (lastPointer !== \"mouse\" || ignored(event.target)) return;\n const doubleTap = action(\"double-tap\");\n if (!doubleTap) return;\n event.preventDefault();\n doubleTap.action();\n };\n\n onMount(() => {\n surface.addEventListener(\"pointerdown\", onPointerDown);\n surface.addEventListener(\"pointermove\", onPointerMove);\n surface.addEventListener(\"pointerup\", onPointerUp);\n surface.addEventListener(\"pointercancel\", onPointerCancel);\n surface.addEventListener(\"touchend\", onTouchEnd, { passive: false });\n surface.addEventListener(\"click\", onClick, true);\n surface.addEventListener(\"contextmenu\", onContextMenu);\n surface.addEventListener(\"mousedown\", onMouseDown);\n surface.addEventListener(\"dblclick\", onDoubleClick);\n onCleanup(() => {\n disposed.abort();\n endPress();\n clearTimeout(settleTimer);\n surface.removeEventListener(\"pointerdown\", onPointerDown);\n surface.removeEventListener(\"pointermove\", onPointerMove);\n surface.removeEventListener(\"pointerup\", onPointerUp);\n surface.removeEventListener(\"pointercancel\", onPointerCancel);\n surface.removeEventListener(\"touchend\", onTouchEnd);\n surface.removeEventListener(\"click\", onClick, true);\n surface.removeEventListener(\"contextmenu\", onContextMenu);\n surface.removeEventListener(\"mousedown\", onMouseDown);\n surface.removeEventListener(\"dblclick\", onDoubleClick);\n });\n });\n\n return (\n <ContextMenu\n items={props.items}\n label={props.label}\n tabIndex={props.tabIndex}\n disabled={!active()}\n onOpen={props.onOpen}\n class={props.class}\n >\n <div\n ref={surface}\n class=\"k2b-gesture-menu\"\n data-active={active() ? \"\" : undefined}\n data-touch={touchAction()}\n data-swipe={swipe()?.direction}\n data-armed={swipe()?.armed ? \"\" : undefined}\n data-settling={swipe()?.settling ? \"\" : undefined}\n style={{\n \"--k2b-gesture-menu-offset\": `${swipe()?.offset ?? 0}px`,\n \"--k2b-gesture-menu-progress\": String(swipe()?.progress ?? 0),\n }}\n >\n <Show when={swipe()}>\n {(current) => (\n <span class=\"k2b-gesture-menu__swipe\" aria-hidden=\"true\">\n <i class={current().icon ?? (current().direction === \"right\" ? \"ti ti-arrow-right\" : \"ti ti-arrow-left\")} />\n </span>\n )}\n </Show>\n <div class=\"k2b-gesture-menu__content\">{props.children}</div>\n </div>\n </ContextMenu>\n );\n}\n\nexport default GestureMenu;\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -4,6 +4,7 @@ export { CopyButton } from "./CopyButton";
|
|
|
4
4
|
export { Disclosure } from "./Disclosure";
|
|
5
5
|
export { Dropdown, DropdownItem, dropdownPosition } from "./Dropdown";
|
|
6
6
|
export { FilterChip } from "./FilterChip";
|
|
7
|
+
export { GestureMenu } from "./GestureMenu";
|
|
7
8
|
export { RemoveButton } from "./RemoveButton";
|
|
8
9
|
export { SegmentedControl } from "./SegmentedControl";
|
|
9
10
|
export { SplitButton } from "./SplitButton";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["actions/index.ts"],"sourcesContent":["export type { ButtonAlign, ButtonLinkProps, ButtonProps, ButtonSize, ButtonVariant, IconButtonLinkProps, IconButtonProps } from \"./Button\";\nexport { Button, ButtonLink, IconButton, IconButtonLink } from \"./Button\";\nexport type { ContextMenuProps } from \"./ContextMenu\";\nexport { ContextMenu } from \"./ContextMenu\";\nexport type { CopyButtonProps, CopyButtonValue } from \"./CopyButton\";\nexport { CopyButton } from \"./CopyButton\";\nexport type { DisclosureProps } from \"./Disclosure\";\nexport { Disclosure } from \"./Disclosure\";\nexport type {\n DropdownAction,\n DropdownActionBase,\n DropdownChoice,\n DropdownItemProps,\n DropdownPosition,\n DropdownProps,\n DropdownSection,\n DropdownTriggerProps,\n} from \"./Dropdown\";\nexport { Dropdown, DropdownItem, dropdownPosition } from \"./Dropdown\";\nexport type { FilterChipOption, FilterChipProps, FilterChipSection } from \"./FilterChip\";\nexport { FilterChip } from \"./FilterChip\";\nexport type { RemoveButtonProps } from \"./RemoveButton\";\nexport { RemoveButton } from \"./RemoveButton\";\nexport type { SegmentedControlProps, SegmentOption } from \"./SegmentedControl\";\nexport { SegmentedControl } from \"./SegmentedControl\";\nexport type { SplitButtonProps } from \"./SplitButton\";\nexport { SplitButton } from \"./SplitButton\";\nexport type {\n SpotlightButtonProps,\n SpotlightButtonVariant,\n SpotlightSearchOptions,\n SpotlightSearchResolver,\n} from \"./SpotlightSearch\";\nexport {\n openSpotlightSearch,\n SpotlightButton,\n} from \"./SpotlightSearch\";\nexport type { TabOption, TabsItemProps, TabsProps } from \"./Tabs\";\nexport { Tabs } from \"./Tabs\";\nexport type { ToolbarGroupProps, ToolbarProps, ToolbarSeparatorProps } from \"./Toolbar\";\nexport { Toolbar } from \"./Toolbar\";\n"],"mappings":"AACA,SAAS,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,QAAQ,UAAU;AAEzE,SAAS,WAAW,QAAQ,eAAe;AAE3C,SAAS,UAAU,QAAQ,cAAc;AAEzC,SAAS,UAAU,QAAQ,cAAc;AAWzC,SAAS,QAAQ,EAAE,YAAY,EAAE,gBAAgB,QAAQ,YAAY;AAErE,SAAS,UAAU,QAAQ,cAAc;AAEzC,SAAS,YAAY,QAAQ,gBAAgB;AAE7C,SAAS,gBAAgB,QAAQ,oBAAoB;AAErD,SAAS,WAAW,QAAQ,eAAe;AAO3C,SACE,mBAAmB,EACnB,eAAe,QACV,mBAAmB;AAE1B,SAAS,IAAI,QAAQ,QAAQ;AAE7B,SAAS,OAAO,QAAQ,WAAW","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["actions/index.ts"],"sourcesContent":["export type { ButtonAlign, ButtonLinkProps, ButtonProps, ButtonSize, ButtonVariant, IconButtonLinkProps, IconButtonProps } from \"./Button\";\nexport { Button, ButtonLink, IconButton, IconButtonLink } from \"./Button\";\nexport type { ContextMenuProps } from \"./ContextMenu\";\nexport { ContextMenu } from \"./ContextMenu\";\nexport type { CopyButtonProps, CopyButtonValue } from \"./CopyButton\";\nexport { CopyButton } from \"./CopyButton\";\nexport type { DisclosureProps } from \"./Disclosure\";\nexport { Disclosure } from \"./Disclosure\";\nexport type {\n DropdownAction,\n DropdownActionBase,\n DropdownChoice,\n DropdownItemProps,\n DropdownPosition,\n DropdownProps,\n DropdownSection,\n DropdownTriggerProps,\n} from \"./Dropdown\";\nexport { Dropdown, DropdownItem, dropdownPosition } from \"./Dropdown\";\nexport type { FilterChipOption, FilterChipProps, FilterChipSection } from \"./FilterChip\";\nexport { FilterChip } from \"./FilterChip\";\nexport type { GestureKind, GestureMenuAction, GestureMenuItem, GestureMenuProps, GestureMenuSection } from \"./GestureMenu\";\nexport { GestureMenu } from \"./GestureMenu\";\nexport type { RemoveButtonProps } from \"./RemoveButton\";\nexport { RemoveButton } from \"./RemoveButton\";\nexport type { SegmentedControlProps, SegmentOption } from \"./SegmentedControl\";\nexport { SegmentedControl } from \"./SegmentedControl\";\nexport type { SplitButtonProps } from \"./SplitButton\";\nexport { SplitButton } from \"./SplitButton\";\nexport type {\n SpotlightButtonProps,\n SpotlightButtonVariant,\n SpotlightSearchOptions,\n SpotlightSearchResolver,\n} from \"./SpotlightSearch\";\nexport {\n openSpotlightSearch,\n SpotlightButton,\n} from \"./SpotlightSearch\";\nexport type { TabOption, TabsItemProps, TabsProps } from \"./Tabs\";\nexport { Tabs } from \"./Tabs\";\nexport type { ToolbarGroupProps, ToolbarProps, ToolbarSeparatorProps } from \"./Toolbar\";\nexport { Toolbar } from \"./Toolbar\";\n"],"mappings":"AACA,SAAS,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,QAAQ,UAAU;AAEzE,SAAS,WAAW,QAAQ,eAAe;AAE3C,SAAS,UAAU,QAAQ,cAAc;AAEzC,SAAS,UAAU,QAAQ,cAAc;AAWzC,SAAS,QAAQ,EAAE,YAAY,EAAE,gBAAgB,QAAQ,YAAY;AAErE,SAAS,UAAU,QAAQ,cAAc;AAEzC,SAAS,WAAW,QAAQ,eAAe;AAE3C,SAAS,YAAY,QAAQ,gBAAgB;AAE7C,SAAS,gBAAgB,QAAQ,oBAAoB;AAErD,SAAS,WAAW,QAAQ,eAAe;AAO3C,SACE,mBAAmB,EACnB,eAAe,QACV,mBAAmB;AAE1B,SAAS,IAAI,QAAQ,QAAQ;AAE7B,SAAS,OAAO,QAAQ,WAAW","ignoreList":[]}
|
|
@@ -49,7 +49,7 @@ import { Tooltip } from "../feedback/Tooltip";
|
|
|
49
49
|
import { replaceTextareaRange } from "../inputs/editor-dom";
|
|
50
50
|
import { completedEmojiShortcode, emojiInTone, emojiName, emojiShortcodeQuery, loadEmojiIndex, suggestEmoji } from "../inputs/emoji/emoji-index";
|
|
51
51
|
import { FileDropTarget } from "../inputs/FileDropTarget";
|
|
52
|
-
import { toggleBulletList,
|
|
52
|
+
import { toggleBulletList, toggleInlineMarker } from "../inputs/markdown/actions";
|
|
53
53
|
import { openCodeFence } from "../inputs/markdown/code-zone";
|
|
54
54
|
import { SelectChip } from "../inputs/SelectChip";
|
|
55
55
|
import { useLocale } from "../intl/locale";
|
|
@@ -62,18 +62,7 @@ const composerMaxInputHeight = 309;
|
|
|
62
62
|
const emojiSuggestionLimit = 8;
|
|
63
63
|
// Keep editor undo text within approximately 2 MiB, plus the current edit.
|
|
64
64
|
const composerUndoBudgetChars = 1_000_000;
|
|
65
|
-
|
|
66
|
-
const {
|
|
67
|
-
value,
|
|
68
|
-
selectionStart,
|
|
69
|
-
selectionEnd
|
|
70
|
-
} = textarea;
|
|
71
|
-
const label = value.slice(selectionStart, selectionEnd);
|
|
72
|
-
replaceTextareaRange(textarea, selectionStart, selectionEnd, `[${label}]()`);
|
|
73
|
-
// Without a selection the label comes first, otherwise the address.
|
|
74
|
-
const caret = label ? selectionStart + label.length + 3 : selectionStart + 1;
|
|
75
|
-
textarea.setSelectionRange(caret, caret);
|
|
76
|
-
};
|
|
65
|
+
/** The row stays short: what a chat message needs. Other Markdown, typed by hand, keeps working. */
|
|
77
66
|
const formatTools = [{
|
|
78
67
|
id: "bold",
|
|
79
68
|
icon: "ti ti-bold",
|
|
@@ -86,39 +75,12 @@ const formatTools = [{
|
|
|
86
75
|
label: m => m.italicShortcut,
|
|
87
76
|
run: textarea => toggleInlineMarker(textarea, "*"),
|
|
88
77
|
key: "i"
|
|
89
|
-
}, {
|
|
90
|
-
id: "strikethrough",
|
|
91
|
-
icon: "ti ti-strikethrough",
|
|
92
|
-
label: m => m.strikethroughShortcut,
|
|
93
|
-
run: textarea => toggleInlineMarker(textarea, "~~"),
|
|
94
|
-
shiftCode: "KeyX"
|
|
95
|
-
}, {
|
|
96
|
-
id: "code",
|
|
97
|
-
icon: "ti ti-code",
|
|
98
|
-
label: m => m.inlineCodeShortcut,
|
|
99
|
-
run: textarea => toggleInlineMarker(textarea, "`"),
|
|
100
|
-
key: "e"
|
|
101
|
-
}, {
|
|
102
|
-
id: "code-block",
|
|
103
|
-
icon: "ti ti-source-code",
|
|
104
|
-
label: m => m.codeBlock,
|
|
105
|
-
run: toggleCodeBlock
|
|
106
78
|
}, {
|
|
107
79
|
id: "bullet-list",
|
|
108
80
|
icon: "ti ti-list",
|
|
109
81
|
label: m => m.bulletListShortcut,
|
|
110
82
|
run: toggleBulletList,
|
|
111
83
|
shiftCode: "Digit8"
|
|
112
|
-
}, {
|
|
113
|
-
id: "quote",
|
|
114
|
-
icon: "ti ti-quote",
|
|
115
|
-
label: m => m.quote,
|
|
116
|
-
run: toggleQuote
|
|
117
|
-
}, {
|
|
118
|
-
id: "link",
|
|
119
|
-
icon: "ti ti-link",
|
|
120
|
-
label: m => m.insertLink,
|
|
121
|
-
run: insertComposerLink
|
|
122
84
|
}];
|
|
123
85
|
const formatShortcut = event => {
|
|
124
86
|
if (event.altKey || !(event.ctrlKey || event.metaKey)) return undefined;
|
|
@@ -735,7 +697,7 @@ export function ChatComposer(props) {
|
|
|
735
697
|
},
|
|
736
698
|
onClick: chooseFiles,
|
|
737
699
|
get children() {
|
|
738
|
-
return _$ssr(_tmpl$3, _$ssrAttribute("class", addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-
|
|
700
|
+
return _$ssr(_tmpl$3, _$ssrAttribute("class", addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-plus", false));
|
|
739
701
|
}
|
|
740
702
|
})
|
|
741
703
|
});
|