@brftech/filex-core 0.41.2 → 0.42.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/{ArchiveViewer-Dq1c4-G5.js → ArchiveViewer-CGl_J_qE.js} +2 -2
- package/dist/{ArchiveViewer-Dq1c4-G5.js.map → ArchiveViewer-CGl_J_qE.js.map} +1 -1
- package/dist/{CsvViewer-CeHslAc0.js → CsvViewer-D-na6SEG.js} +2 -2
- package/dist/{CsvViewer-CeHslAc0.js.map → CsvViewer-D-na6SEG.js.map} +1 -1
- package/dist/{DrawioViewer-p03u7Oda.js → DrawioViewer-BFkTSrNc.js} +2 -2
- package/dist/{DrawioViewer-p03u7Oda.js.map → DrawioViewer-BFkTSrNc.js.map} +1 -1
- package/dist/{EpubViewer-D08Y-Tww.js → EpubViewer-BfRE0DTL.js} +2 -2
- package/dist/{EpubViewer-D08Y-Tww.js.map → EpubViewer-BfRE0DTL.js.map} +1 -1
- package/dist/{IpynbViewer-mG7p3Sil.js → IpynbViewer-BpBlICH-.js} +2 -2
- package/dist/{IpynbViewer-mG7p3Sil.js.map → IpynbViewer-BpBlICH-.js.map} +1 -1
- package/dist/{MermaidViewer-CCPg1Q3W.js → MermaidViewer-Ry5Ma463.js} +2 -2
- package/dist/{MermaidViewer-CCPg1Q3W.js.map → MermaidViewer-Ry5Ma463.js.map} +1 -1
- package/dist/{PsdViewer-CDEHTiUN.js → PsdViewer-BmTlq1Gh.js} +2 -2
- package/dist/{PsdViewer-CDEHTiUN.js.map → PsdViewer-BmTlq1Gh.js.map} +1 -1
- package/dist/{TiffViewer-DnWRUGOK.js → TiffViewer-DE5bKDDx.js} +2 -2
- package/dist/{TiffViewer-DnWRUGOK.js.map → TiffViewer-DE5bKDDx.js.map} +1 -1
- package/dist/{Viewer3D-COCQp53D.js → Viewer3D-DefcNYOm.js} +2 -2
- package/dist/{Viewer3D-COCQp53D.js.map → Viewer3D-DefcNYOm.js.map} +1 -1
- package/dist/filex-core.js +1 -1
- package/dist/filex-core.umd.cjs +46 -46
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/{index-JUiZzLmF.js → index-B0N5qiyj.js} +9234 -9148
- package/dist/index-B0N5qiyj.js.map +1 -0
- package/dist/index.d.ts +59 -4
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/FileExplorer.vue +19 -0
- package/src/components/FilePane.vue +34 -31
- package/src/components/GalleryView.vue +44 -8
- package/src/components/GridView.vue +58 -19
- package/src/components/HomeView.vue +5 -4
- package/src/components/ItemCheck.vue +47 -0
- package/src/components/ListView.vue +51 -48
- package/src/composables/useRowTouch.ts +85 -35
- package/src/styles/base.css +111 -6
- package/src/types/ExplorerConfig.ts +32 -0
- package/dist/index-JUiZzLmF.js.map +0 -1
|
@@ -1,34 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* useRowTouch — the one touch grammar every file view speaks (list,
|
|
3
|
-
* gallery).
|
|
2
|
+
* useRowTouch — the one click and touch grammar every file view speaks (list,
|
|
3
|
+
* grid, gallery).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `dblclick` at all, so on a phone the desktop grammar left nothing openable
|
|
8
|
-
* (issue #26). What a finger does instead is what every mobile file manager
|
|
9
|
-
* does:
|
|
5
|
+
* The rule (issue #26, fourth round — the reporter's words: "only clicking on
|
|
6
|
+
* checkbox selects it, any other click will open"), the same on every device:
|
|
10
7
|
*
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
8
|
+
* - the item's CHECKBOX selects: a tick adds or removes, shift+tick extends
|
|
9
|
+
* the range from the anchor;
|
|
10
|
+
* - a click or tap ANYWHERE ELSE on the item opens it — the name, the icon,
|
|
11
|
+
* the size column, the empty space beside the name, with or without a
|
|
12
|
+
* selection, with or without Ctrl/Shift held;
|
|
13
|
+
* - a right click (mouse) or a long press (finger) opens the item's menu;
|
|
14
|
+
* - the item's own controls — the star, the ⋮ button — do their own job.
|
|
13
15
|
*
|
|
14
|
-
* The views only report. What a
|
|
16
|
+
* The views only report. What a click means is decided once, in FilePane.
|
|
15
17
|
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
19
|
-
* - a
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* How the rule got here:
|
|
19
|
+
* - First round: a finger has no double-click, so on a phone nothing could
|
|
20
|
+
* be opened at all. A tap was made to open.
|
|
21
|
+
* - Second round: a press on the NAME opened on every device, a press
|
|
22
|
+
* beside it still selected. The reporter: "need to click precisely on
|
|
23
|
+
* name, if a lil bit on the right then it selects file".
|
|
24
|
+
* - Third round: on a phone the name still needed two taps. A tap reaches a
|
|
25
|
+
* page as a click only at the end of the browser's compatibility sequence
|
|
26
|
+
* (touch → mouse move → hover → mouse down/up → click), and iOS WebKit
|
|
27
|
+
* stops that sequence when the hover step reveals content. So a finger
|
|
28
|
+
* lifted on the item opens it right there, at `touchend`, and cancels the
|
|
29
|
+
* emulated mouse events that would follow (`onTap`); the hover reveals are
|
|
30
|
+
* kept off screens that cannot hover (styles/base.css,
|
|
31
|
+
* `@media (hover: hover)`).
|
|
32
|
+
* - Fourth round: the checkbox became the only way a click selects, and the
|
|
33
|
+
* grid and gallery cards got one too (they had none).
|
|
22
34
|
*
|
|
23
35
|
* ⚠ A tap is judged from the gesture that produced the click, never from the
|
|
24
36
|
* screen: `pointerType` where the browser sets it on click (Chromium, Firefox),
|
|
25
37
|
* and the touchend that just preceded the click where it does not (older
|
|
26
|
-
* WebKit). A touch laptop's trackpad
|
|
27
|
-
*
|
|
28
|
-
* those two apart.
|
|
38
|
+
* WebKit). A touch laptop's trackpad and its screen are two different gestures
|
|
39
|
+
* a `(pointer: coarse)` media query cannot tell apart.
|
|
29
40
|
*
|
|
30
41
|
* ⚠ It used to live as three identical copies of the long-press timer, one per
|
|
31
|
-
* view;
|
|
42
|
+
* view; a rule like this one is exactly the kind of addition a copy misses.
|
|
32
43
|
*/
|
|
33
44
|
import { onBeforeUnmount } from 'vue';
|
|
34
45
|
|
|
@@ -39,6 +50,15 @@ const TAP_WINDOW_MS = 800;
|
|
|
39
50
|
/** A finger that travels further than this is scrolling, not pressing. */
|
|
40
51
|
const MOVE_TOLERANCE_PX = 10;
|
|
41
52
|
|
|
53
|
+
/**
|
|
54
|
+
* What on an item is a control of its own: a press there is the control's,
|
|
55
|
+
* not the item's. The checkbox is one (it selects on its own click), and so
|
|
56
|
+
* are the star and the ⋮ button. A wrapper cell that swallows its clicks
|
|
57
|
+
* carries `data-fe-control` so a finger on its padding is not an open either.
|
|
58
|
+
*/
|
|
59
|
+
export const ITEM_CONTROL_SELECTOR =
|
|
60
|
+
'button, a, input, select, textarea, label, [role="checkbox"], [data-fe-control]';
|
|
61
|
+
|
|
42
62
|
export interface TouchPoint {
|
|
43
63
|
clientX: number;
|
|
44
64
|
clientY: number;
|
|
@@ -50,30 +70,47 @@ export interface ClickMod {
|
|
|
50
70
|
shift: boolean;
|
|
51
71
|
/** The click was a finger's tap. */
|
|
52
72
|
touch?: boolean;
|
|
53
|
-
/** The click
|
|
54
|
-
|
|
73
|
+
/** The click was on the item's checkbox — the one click that selects. */
|
|
74
|
+
check?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The one place a view turns a click on an item into a `ClickMod`. */
|
|
78
|
+
export function clickMod(ev: MouseEvent, touch: boolean): ClickMod {
|
|
79
|
+
return { ctrl: ev.ctrlKey || ev.metaKey, shift: ev.shiftKey, touch };
|
|
55
80
|
}
|
|
56
81
|
|
|
57
82
|
/**
|
|
58
|
-
* The one place a view turns a click into a `ClickMod`.
|
|
59
|
-
*
|
|
83
|
+
* The one place a view turns a click on an item's CHECKBOX into a `ClickMod`.
|
|
84
|
+
* `ctrl` because a tick adds to a selection rather than replacing it; `shift`
|
|
85
|
+
* passes through so a shift-tick still extends the range from the anchor.
|
|
60
86
|
*/
|
|
61
|
-
export function
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
ctrl: ev.ctrlKey || ev.metaKey,
|
|
65
|
-
shift: ev.shiftKey,
|
|
66
|
-
touch,
|
|
67
|
-
name: typeof target?.closest === 'function' && target.closest(nameSelector) !== null,
|
|
68
|
-
};
|
|
87
|
+
export function checkMod(ev: MouseEvent): ClickMod {
|
|
88
|
+
return { ctrl: true, shift: ev.shiftKey, check: true };
|
|
69
89
|
}
|
|
70
90
|
|
|
71
|
-
export
|
|
91
|
+
export interface RowTouchOptions<T> {
|
|
92
|
+
/** A tap on the item outside its controls: open it, before any click. */
|
|
93
|
+
onTap?: (item: T) => void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Whether the press started on one of the item's own controls. */
|
|
97
|
+
function onControl(ev: Event): boolean {
|
|
98
|
+
const el = ev.target as Element | null;
|
|
99
|
+
if (typeof el?.closest !== 'function') return false;
|
|
100
|
+
const control = el.closest(ITEM_CONTROL_SELECTOR);
|
|
101
|
+
const item = ev.currentTarget as Element | null;
|
|
102
|
+
// Only a control INSIDE the item counts: the listing itself may sit in a
|
|
103
|
+
// label or a link on some host page, and that must not disarm every tap.
|
|
104
|
+
return control !== null && (item === null || typeof item.contains !== 'function' || item.contains(control));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function useRowTouch<T>(onLongPress: (item: T, at: TouchPoint) => void, options: RowTouchOptions<T> = {}) {
|
|
72
108
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
73
109
|
let target: T | null = null;
|
|
74
110
|
let origin: TouchPoint = { clientX: 0, clientY: 0 };
|
|
75
111
|
let longPressed = false;
|
|
76
112
|
let tapEndedAt = 0;
|
|
113
|
+
let startedOnItem = false;
|
|
77
114
|
|
|
78
115
|
function stopTimer() {
|
|
79
116
|
if (timer) clearTimeout(timer);
|
|
@@ -87,6 +124,7 @@ export function useRowTouch<T>(onLongPress: (item: T, at: TouchPoint) => void) {
|
|
|
87
124
|
target = item;
|
|
88
125
|
longPressed = false;
|
|
89
126
|
origin = { clientX: t0.clientX, clientY: t0.clientY };
|
|
127
|
+
startedOnItem = !onControl(ev);
|
|
90
128
|
timer = setTimeout(() => {
|
|
91
129
|
timer = undefined;
|
|
92
130
|
if (target === null) return;
|
|
@@ -107,10 +145,22 @@ export function useRowTouch<T>(onLongPress: (item: T, at: TouchPoint) => void) {
|
|
|
107
145
|
}
|
|
108
146
|
}
|
|
109
147
|
|
|
110
|
-
|
|
148
|
+
/**
|
|
149
|
+
* ⚠ Bind it WITHOUT `.passive`: a passive listener cannot cancel the
|
|
150
|
+
* emulated mouse events, and the click they end in would then reach the new
|
|
151
|
+
* listing under the finger (FilePane's open guard catches that too).
|
|
152
|
+
*/
|
|
153
|
+
function onTouchEnd(ev?: TouchEvent) {
|
|
111
154
|
stopTimer();
|
|
112
|
-
|
|
155
|
+
const item = target;
|
|
156
|
+
const tapped = item !== null && !longPressed;
|
|
157
|
+
if (tapped) tapEndedAt = Date.now();
|
|
113
158
|
target = null;
|
|
159
|
+
if (tapped && startedOnItem && options.onTap) {
|
|
160
|
+
if (ev?.cancelable) ev.preventDefault();
|
|
161
|
+
options.onTap(item as T);
|
|
162
|
+
}
|
|
163
|
+
startedOnItem = false;
|
|
114
164
|
}
|
|
115
165
|
|
|
116
166
|
/** Whether this click is a finger's tap. */
|
package/src/styles/base.css
CHANGED
|
@@ -466,11 +466,22 @@ filex-explorer {
|
|
|
466
466
|
opacity: 0;
|
|
467
467
|
transition: opacity 0.12s;
|
|
468
468
|
}
|
|
469
|
-
|
|
469
|
+
/* issue #26, third round — a reveal ON HOVER only where hovering exists.
|
|
470
|
+
iOS WebKit treats a tap whose :hover styles reveal content (an opacity
|
|
471
|
+
transition from 0 counts) as "the user wanted to see the hover state": it
|
|
472
|
+
applies the hover and swallows the click, so the first tap on a row or card
|
|
473
|
+
only highlighted it and a second tap was needed to open. `(hover: hover)`
|
|
474
|
+
keeps these reveals off touch screens, where they could never be hovered on
|
|
475
|
+
purpose anyway. `:focus-within` and a starred item stay visible everywhere. */
|
|
470
476
|
.fe-list__row:focus-within .fe-list__col--star,
|
|
471
477
|
.fe-list__col--star:has(.is-starred) {
|
|
472
478
|
opacity: 1;
|
|
473
479
|
}
|
|
480
|
+
@media (hover: hover) {
|
|
481
|
+
.fe-list__row:hover .fe-list__col--star {
|
|
482
|
+
opacity: 1;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
474
485
|
.fe-list__col--star .filex-star-btn.is-starred {
|
|
475
486
|
color: var(--fe-warning, #f59e0b);
|
|
476
487
|
}
|
|
@@ -3683,11 +3694,25 @@ filex-explorer {
|
|
|
3683
3694
|
transition: opacity 0.12s ease, transform 0.12s ease;
|
|
3684
3695
|
pointer-events: none;
|
|
3685
3696
|
}
|
|
3686
|
-
.fe-gal__card:hover .fe-gal__meta,
|
|
3687
3697
|
.fe-gal__card:focus-visible .fe-gal__meta {
|
|
3688
3698
|
opacity: 1;
|
|
3689
3699
|
transform: translateY(0);
|
|
3690
3700
|
}
|
|
3701
|
+
/* issue #26 — see the note on .fe-list__col--star: no hover reveal on touch.
|
|
3702
|
+
A screen that cannot hover shows the caption outright instead, since it
|
|
3703
|
+
would otherwise never be seen there. */
|
|
3704
|
+
@media (hover: hover) {
|
|
3705
|
+
.fe-gal__card:hover .fe-gal__meta {
|
|
3706
|
+
opacity: 1;
|
|
3707
|
+
transform: translateY(0);
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
@media (hover: none) {
|
|
3711
|
+
.fe-gal__meta {
|
|
3712
|
+
opacity: 1;
|
|
3713
|
+
transform: none;
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3691
3716
|
.fe-gal__meta-line {
|
|
3692
3717
|
overflow: hidden;
|
|
3693
3718
|
text-overflow: ellipsis;
|
|
@@ -4560,9 +4585,7 @@ filex-explorer {
|
|
|
4560
4585
|
opacity: 0;
|
|
4561
4586
|
transition: opacity 120ms ease;
|
|
4562
4587
|
}
|
|
4563
|
-
.fe-grid__card:hover .fe-grid__star,
|
|
4564
4588
|
.fe-grid__card:focus-within .fe-grid__star,
|
|
4565
|
-
.fe-gal__card:hover .fe-gal__star,
|
|
4566
4589
|
.fe-gal__card:focus-within .fe-gal__star,
|
|
4567
4590
|
.fe-grid__star:focus-within,
|
|
4568
4591
|
.fe-gal__star:focus-within,
|
|
@@ -4570,12 +4593,88 @@ filex-explorer {
|
|
|
4570
4593
|
.fe-gal__star:has(.is-starred) {
|
|
4571
4594
|
opacity: 1;
|
|
4572
4595
|
}
|
|
4596
|
+
/* issue #26 — see the note on .fe-list__col--star: no hover reveal on touch. */
|
|
4597
|
+
@media (hover: hover) {
|
|
4598
|
+
.fe-grid__card:hover .fe-grid__star,
|
|
4599
|
+
.fe-gal__card:hover .fe-gal__star {
|
|
4600
|
+
opacity: 1;
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4573
4603
|
/* `:has()` is the modern half of the rule above; this is the same thing for
|
|
4574
4604
|
engines without it — a starred chip paints itself. Both are needed: the
|
|
4575
4605
|
parent wrapper carries the opacity, the child cannot raise it on its own. */
|
|
4576
4606
|
.filex-star-btn.is-card.is-starred {
|
|
4577
4607
|
opacity: 1;
|
|
4578
4608
|
}
|
|
4609
|
+
/* === issue #26 — the checkbox on a grid / gallery card ================
|
|
4610
|
+
The one click on a card that selects; a click anywhere else opens it. The
|
|
4611
|
+
box itself is ItemCheck (.fe-list__check, the list's own tick). It shows
|
|
4612
|
+
when the card is hovered (where hovering exists — see the star note), when
|
|
4613
|
+
something inside the card is focused, when the card is selected, and on
|
|
4614
|
+
every card once anything is selected — which is how a finger, with no
|
|
4615
|
+
hover, finds the next box to tick after a long press selected the first.
|
|
4616
|
+
⚠ Hidden means `pointer-events: none` as well: an invisible box would take
|
|
4617
|
+
the tap meant to open the card under it. */
|
|
4618
|
+
.fe-item-check {
|
|
4619
|
+
display: flex;
|
|
4620
|
+
align-items: center;
|
|
4621
|
+
justify-content: center;
|
|
4622
|
+
opacity: 0;
|
|
4623
|
+
pointer-events: none;
|
|
4624
|
+
transition: opacity 120ms ease;
|
|
4625
|
+
}
|
|
4626
|
+
.fe-grid__card.is-selected .fe-item-check,
|
|
4627
|
+
.fe-gal__card.is-selected .fe-item-check,
|
|
4628
|
+
.fe-grid.has-selection .fe-item-check,
|
|
4629
|
+
.fe-gal.has-selection .fe-item-check,
|
|
4630
|
+
.fe-grid__card:focus-within .fe-item-check,
|
|
4631
|
+
.fe-gal__card:focus-within .fe-item-check {
|
|
4632
|
+
opacity: 1;
|
|
4633
|
+
pointer-events: auto;
|
|
4634
|
+
}
|
|
4635
|
+
@media (hover: hover) {
|
|
4636
|
+
.fe-grid__card:hover .fe-item-check,
|
|
4637
|
+
.fe-gal__card:hover .fe-item-check {
|
|
4638
|
+
opacity: 1;
|
|
4639
|
+
pointer-events: auto;
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
/* On a grid card the box takes the type tile's 24px slot in the card's row,
|
|
4643
|
+
so the name does not move when it appears; the tile steps aside. */
|
|
4644
|
+
.fe-grid__lead {
|
|
4645
|
+
position: relative;
|
|
4646
|
+
flex: 0 0 24px;
|
|
4647
|
+
width: 24px;
|
|
4648
|
+
height: 24px;
|
|
4649
|
+
display: inline-flex;
|
|
4650
|
+
align-items: center;
|
|
4651
|
+
justify-content: center;
|
|
4652
|
+
}
|
|
4653
|
+
.fe-grid__lead > .fe-item-check {
|
|
4654
|
+
position: absolute;
|
|
4655
|
+
inset: 0;
|
|
4656
|
+
}
|
|
4657
|
+
.fe-grid__lead > .fe-grid__tile {
|
|
4658
|
+
transition: opacity 120ms ease;
|
|
4659
|
+
}
|
|
4660
|
+
.fe-grid__card.is-selected .fe-grid__lead--pick > .fe-grid__tile,
|
|
4661
|
+
.fe-grid.has-selection .fe-grid__lead--pick > .fe-grid__tile,
|
|
4662
|
+
.fe-grid__card:focus-within .fe-grid__lead--pick > .fe-grid__tile {
|
|
4663
|
+
opacity: 0;
|
|
4664
|
+
}
|
|
4665
|
+
@media (hover: hover) {
|
|
4666
|
+
.fe-grid__card:hover .fe-grid__lead--pick > .fe-grid__tile {
|
|
4667
|
+
opacity: 0;
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
/* On a gallery card it is the thumbnail's top-left corner, opposite the star. */
|
|
4671
|
+
.fe-gal__check {
|
|
4672
|
+
position: absolute;
|
|
4673
|
+
top: 6px;
|
|
4674
|
+
left: 6px;
|
|
4675
|
+
z-index: 2;
|
|
4676
|
+
}
|
|
4677
|
+
|
|
4579
4678
|
/* The chip itself: a round translucent puck, legible over a photo thumbnail
|
|
4580
4679
|
as well as over a flat icon tile. */
|
|
4581
4680
|
.filex-star-btn.is-card {
|
|
@@ -5932,8 +6031,9 @@ filex-explorer {
|
|
|
5932
6031
|
border-bottom: 1px solid var(--fe-border-soft);
|
|
5933
6032
|
}
|
|
5934
6033
|
|
|
5935
|
-
/* The tick
|
|
5936
|
-
|
|
6034
|
+
/* The tick — the one click on an item that selects (issue #26; ListView
|
|
6035
|
+
onCheckClick, components/ItemCheck). The plain control look, not an accent
|
|
6036
|
+
of its own. */
|
|
5937
6037
|
.fe-list__col--check {
|
|
5938
6038
|
display: flex;
|
|
5939
6039
|
align-items: center;
|
|
@@ -10285,6 +10385,11 @@ filex-explorer {
|
|
|
10285
10385
|
.fe-list__col--star {
|
|
10286
10386
|
justify-content: center;
|
|
10287
10387
|
}
|
|
10388
|
+
/* issue #26 — the star's click guard (ListView): stops the click without a
|
|
10389
|
+
box of its own, so the star sits where it always did. */
|
|
10390
|
+
.fe-list__star-hit {
|
|
10391
|
+
display: contents;
|
|
10392
|
+
}
|
|
10288
10393
|
/* Location was never in the muted-value rule above it — it is a value like
|
|
10289
10394
|
Type and Owner and reads like one. */
|
|
10290
10395
|
.fe-list__col--location {
|
|
@@ -454,6 +454,38 @@ export interface ExplorerConfig {
|
|
|
454
454
|
callerKind?: 'user' | 'app';
|
|
455
455
|
|
|
456
456
|
|
|
457
|
+
/**
|
|
458
|
+
* How a mouse opens an item.
|
|
459
|
+
*
|
|
460
|
+
* 'double' (default) — a single click SELECTS, a double click OPENS
|
|
461
|
+
* (classic desktop file-manager). This is a
|
|
462
|
+
* per-viewer preference the host may expose as a
|
|
463
|
+
* setting.
|
|
464
|
+
* 'single' — a single click OPENS (the item is opened on the
|
|
465
|
+
* first click; the checkbox is still the one click
|
|
466
|
+
* that selects).
|
|
467
|
+
*
|
|
468
|
+
* ⚠ TOUCH is not governed by this: a finger tap always opens (the mobile
|
|
469
|
+
* convention, and there is no hover-to-select on a touchscreen). The
|
|
470
|
+
* checkbox always selects, on every device and in either mode. See
|
|
471
|
+
* FilePane.onViewClick + composables/useRowTouch.
|
|
472
|
+
*/
|
|
473
|
+
openTrigger?: 'single' | 'double';
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Hand the OPEN of a file to the host instead of opening the in-page
|
|
477
|
+
* preview/editor overlay. When true, opening a file emits `file-opened`
|
|
478
|
+
* (path + basename) and the explorer does NOT mount its own PreviewModal —
|
|
479
|
+
* the host decides what to do (the desktop app opens the file in its own
|
|
480
|
+
* window per document). Directories still navigate inline, and Space
|
|
481
|
+
* quick-look still peeks in-page. Default false (web/embeds preview inline).
|
|
482
|
+
*
|
|
483
|
+
* ⚠ Ignored inside an unlocked E2E-encrypted folder: the host window would
|
|
484
|
+
* fetch raw server bytes (ciphertext), so those keep the in-page decrypted
|
|
485
|
+
* preview.
|
|
486
|
+
*/
|
|
487
|
+
openInHost?: boolean;
|
|
488
|
+
|
|
457
489
|
/** Default view. */
|
|
458
490
|
viewMode?: 'list' | 'grid';
|
|
459
491
|
|