@marimo-team/islands 0.21.2-dev69 → 0.21.2-dev71
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/package.json
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { isInteractiveTarget } from "../use-cell-range-selection";
|
|
5
|
+
|
|
6
|
+
function createMouseEvent(
|
|
7
|
+
target: HTMLElement,
|
|
8
|
+
currentTarget: HTMLElement,
|
|
9
|
+
): React.MouseEvent {
|
|
10
|
+
return { target, currentTarget } as unknown as React.MouseEvent;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("isInteractiveTarget", () => {
|
|
14
|
+
it("returns false when target is the cell itself", () => {
|
|
15
|
+
const cell = document.createElement("td");
|
|
16
|
+
expect(isInteractiveTarget(createMouseEvent(cell, cell))).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("returns false when clicking plain text inside a cell", () => {
|
|
20
|
+
const cell = document.createElement("td");
|
|
21
|
+
const span = document.createElement("span");
|
|
22
|
+
cell.append(span);
|
|
23
|
+
expect(isInteractiveTarget(createMouseEvent(span, cell))).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it.each([
|
|
27
|
+
"input",
|
|
28
|
+
"button",
|
|
29
|
+
"select",
|
|
30
|
+
"textarea",
|
|
31
|
+
])("returns true when clicking a <%s>", (tag) => {
|
|
32
|
+
const cell = document.createElement("td");
|
|
33
|
+
const el = document.createElement(tag);
|
|
34
|
+
cell.append(el);
|
|
35
|
+
expect(isInteractiveTarget(createMouseEvent(el, cell))).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns true when clicking an <a> link", () => {
|
|
39
|
+
const cell = document.createElement("td");
|
|
40
|
+
const a = document.createElement("a");
|
|
41
|
+
a.href = "#";
|
|
42
|
+
cell.append(a);
|
|
43
|
+
expect(isInteractiveTarget(createMouseEvent(a, cell))).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("returns true when clicking a <label>", () => {
|
|
47
|
+
const cell = document.createElement("td");
|
|
48
|
+
const label = document.createElement("label");
|
|
49
|
+
cell.append(label);
|
|
50
|
+
expect(isInteractiveTarget(createMouseEvent(label, cell))).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns true for element with role="checkbox"', () => {
|
|
54
|
+
const cell = document.createElement("td");
|
|
55
|
+
const div = document.createElement("div");
|
|
56
|
+
div.setAttribute("role", "checkbox");
|
|
57
|
+
cell.append(div);
|
|
58
|
+
expect(isInteractiveTarget(createMouseEvent(div, cell))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('returns true for element with role="button"', () => {
|
|
62
|
+
const cell = document.createElement("td");
|
|
63
|
+
const div = document.createElement("div");
|
|
64
|
+
div.setAttribute("role", "button");
|
|
65
|
+
cell.append(div);
|
|
66
|
+
expect(isInteractiveTarget(createMouseEvent(div, cell))).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns true for contenteditable="true"', () => {
|
|
70
|
+
const cell = document.createElement("td");
|
|
71
|
+
const div = document.createElement("div");
|
|
72
|
+
div.setAttribute("contenteditable", "true");
|
|
73
|
+
cell.append(div);
|
|
74
|
+
expect(isInteractiveTarget(createMouseEvent(div, cell))).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns true when clicking a child nested inside an interactive element", () => {
|
|
78
|
+
const cell = document.createElement("td");
|
|
79
|
+
const button = document.createElement("button");
|
|
80
|
+
const icon = document.createElement("span");
|
|
81
|
+
button.append(icon);
|
|
82
|
+
cell.append(button);
|
|
83
|
+
expect(isInteractiveTarget(createMouseEvent(icon, cell))).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("returns true when clicking inside a marimo-ui-element", () => {
|
|
87
|
+
const cell = document.createElement("td");
|
|
88
|
+
const marimoEl = document.createElement("marimo-ui-element");
|
|
89
|
+
const inner = document.createElement("div");
|
|
90
|
+
marimoEl.append(inner);
|
|
91
|
+
cell.append(marimoEl);
|
|
92
|
+
expect(isInteractiveTarget(createMouseEvent(inner, cell))).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("returns true when clicking the marimo-ui-element itself", () => {
|
|
96
|
+
const cell = document.createElement("td");
|
|
97
|
+
const marimoEl = document.createElement("marimo-ui-element");
|
|
98
|
+
cell.append(marimoEl);
|
|
99
|
+
expect(isInteractiveTarget(createMouseEvent(marimoEl, cell))).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("returns false when clicking a non-interactive div", () => {
|
|
103
|
+
const cell = document.createElement("td");
|
|
104
|
+
const wrapper = document.createElement("div");
|
|
105
|
+
const text = document.createElement("span");
|
|
106
|
+
wrapper.append(text);
|
|
107
|
+
cell.append(wrapper);
|
|
108
|
+
expect(isInteractiveTarget(createMouseEvent(text, cell))).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("returns false when target is a non-Element (e.g. Text node)", () => {
|
|
112
|
+
const cell = document.createElement("td");
|
|
113
|
+
const textNode = document.createTextNode("hello");
|
|
114
|
+
cell.append(textNode);
|
|
115
|
+
expect(isInteractiveTarget(createMouseEvent(textNode as never, cell))).toBe(
|
|
116
|
+
false,
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -91,6 +91,10 @@ export const useCellRangeSelection = <TData>({
|
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
if (isInteractiveTarget(e)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
94
98
|
actions.handleCellMouseDown({
|
|
95
99
|
cell,
|
|
96
100
|
isShiftKey: e.shiftKey,
|
|
@@ -122,3 +126,18 @@ export const useCellRangeSelection = <TData>({
|
|
|
122
126
|
clearSelection: actions.clearSelection,
|
|
123
127
|
};
|
|
124
128
|
};
|
|
129
|
+
|
|
130
|
+
const INTERACTIVE_SELECTOR =
|
|
131
|
+
'input, button, select, textarea, a, label, [role="checkbox"], [role="button"], [contenteditable="true"], marimo-ui-element';
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Skip cell selection when the click target is inside an interactive element
|
|
135
|
+
* (e.g. a checkbox or button rendered as rich cell content).
|
|
136
|
+
*/
|
|
137
|
+
export function isInteractiveTarget(e: React.MouseEvent): boolean {
|
|
138
|
+
const target = e.target;
|
|
139
|
+
if (target === e.currentTarget || !(target instanceof Element)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return target.closest(INTERACTIVE_SELECTOR) !== null;
|
|
143
|
+
}
|
package/src/css/md.css
CHANGED
|
@@ -12,6 +12,16 @@
|
|
|
12
12
|
margin-block-end: 0;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.markdown ul li,
|
|
16
|
+
.markdown ol li {
|
|
17
|
+
margin-block: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Restore browser default bullet cycling that Tailwind Typography overrides */
|
|
21
|
+
.markdown ul ul {
|
|
22
|
+
list-style-type: revert;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
.markdown h1 {
|
|
16
26
|
text-align: start;
|
|
17
27
|
}
|