@crisp-ui-kit/crisp 0.20.0 → 0.20.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.
- package/dist/appshell.css +2 -2
- package/dist/cjs/core/direction.js +25 -0
- package/dist/cjs/core/index.js +3 -1
- package/dist/cjs/core/place-floating.js +2 -19
- package/dist/cjs/product/appshell/appshell.js +7 -1
- package/dist/cjs/product/appshell/appshell.recipe.js +2 -2
- package/dist/cjs/product/recordpage/recordpage.js +9 -2
- package/dist/cjs/product/recordpage/recordpage.recipe.js +2 -2
- package/dist/esm/core/direction.js +22 -0
- package/dist/esm/core/index.js +1 -0
- package/dist/esm/core/place-floating.js +2 -19
- package/dist/esm/product/appshell/appshell.js +7 -1
- package/dist/esm/product/appshell/appshell.recipe.js +2 -2
- package/dist/esm/product/recordpage/recordpage.js +9 -2
- package/dist/esm/product/recordpage/recordpage.recipe.js +2 -2
- package/dist/recordpage.css +2 -2
- package/dist/styles.css +4 -4
- package/dist/types/core/direction.d.ts +10 -0
- package/dist/types/core/index.d.ts +1 -0
- package/dist/types/core/place-floating.d.ts +20 -0
- package/package.json +1 -1
package/dist/appshell.css
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
display: flex; height: 100%; width: 100%; overflow: hidden; background: var(--bg-surface); font-family: var(--font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
4
4
|
& .appshell-side { position: relative; flex-shrink: 0; overflow: hidden; }
|
|
5
5
|
& .appshell-side-inner { height: 100%; }
|
|
6
|
-
& .appshell-resize { position: absolute; top: 0;
|
|
7
|
-
& .appshell-resize::after { content: ""; position: absolute; top: 0;
|
|
6
|
+
& .appshell-resize { position: absolute; top: 0; inset-inline-end: 0; width: 10px; height: 100%; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
7
|
+
& .appshell-resize::after { content: ""; position: absolute; top: 0; inset-inline-end: 0; width: 1px; height: 100%; background: var(--border-subtle); transition: background-color .16s, height .16s; }
|
|
8
8
|
& .appshell-resize:hover::after, & .appshell-resize[data-dragging]::after { width: 2px; background: var(--bg-brand-solid); }
|
|
9
9
|
& .appshell-main { position: relative; flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
10
10
|
& .appshell-expand { position: absolute; top: var(--space-2); inset-inline-start: var(--space-2); z-index: 6; display: inline-flex; align-items: center; justify-content: center; width: var(--size-row); height: var(--size-row); padding: 0; border: none; border-radius: var(--radius-md); box-shadow: inset 0 0 0 1px var(--border-subtle); background: var(--bg-surface); color: var(--fg-muted); font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isRtlElement = isRtlElement;
|
|
4
|
+
/**
|
|
5
|
+
* The nearest declared direction. The `dir` attribute is checked first because
|
|
6
|
+
* it is how a document says this in practice and it is readable without a
|
|
7
|
+
* layout, then computed style for a direction set purely in CSS.
|
|
8
|
+
*
|
|
9
|
+
* Shared so that a floating layer's edge choice and a drag handler's sign come
|
|
10
|
+
* from the same answer. They have to agree: mirroring one without the other is
|
|
11
|
+
* the half-conversion lesson 54 is about.
|
|
12
|
+
*/
|
|
13
|
+
function isRtlElement(anchor) {
|
|
14
|
+
if (!anchor || typeof window === "undefined")
|
|
15
|
+
return false;
|
|
16
|
+
const declared = anchor.closest("[dir]")?.getAttribute("dir");
|
|
17
|
+
if (declared)
|
|
18
|
+
return declared.toLowerCase() === "rtl";
|
|
19
|
+
try {
|
|
20
|
+
return window.getComputedStyle(anchor).direction === "rtl";
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/cjs/core/index.js
CHANGED
|
@@ -14,11 +14,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.usePrefersReducedMotion = exports.resolveRecipe = exports.Primitive = exports.placeFloating = exports.defineRecipe = exports.cx = void 0;
|
|
17
|
+
exports.usePrefersReducedMotion = exports.resolveRecipe = exports.Primitive = exports.placeFloating = exports.isRtlElement = exports.defineRecipe = exports.cx = void 0;
|
|
18
18
|
var cx_1 = require("./cx");
|
|
19
19
|
Object.defineProperty(exports, "cx", { enumerable: true, get: function () { return cx_1.cx; } });
|
|
20
20
|
var define_1 = require("./define");
|
|
21
21
|
Object.defineProperty(exports, "defineRecipe", { enumerable: true, get: function () { return define_1.defineRecipe; } });
|
|
22
|
+
var direction_1 = require("./direction");
|
|
23
|
+
Object.defineProperty(exports, "isRtlElement", { enumerable: true, get: function () { return direction_1.isRtlElement; } });
|
|
22
24
|
var place_floating_1 = require("./place-floating");
|
|
23
25
|
Object.defineProperty(exports, "placeFloating", { enumerable: true, get: function () { return place_floating_1.placeFloating; } });
|
|
24
26
|
var primitive_1 = require("./primitive");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.placeFloating = placeFloating;
|
|
4
|
+
const direction_1 = require("./direction");
|
|
4
5
|
/**
|
|
5
6
|
* Where a floating layer goes, in logical terms.
|
|
6
7
|
*
|
|
@@ -21,26 +22,8 @@ exports.placeFloating = placeFloating;
|
|
|
21
22
|
* be the half-conversion #146 exists to prevent — the rect it is measured
|
|
22
23
|
* against would still be physical.
|
|
23
24
|
*/
|
|
24
|
-
/**
|
|
25
|
-
* The nearest declared direction. The `dir` attribute is checked first because
|
|
26
|
-
* it is how a document says this in practice and it is readable without a
|
|
27
|
-
* layout, then computed style for a direction set purely in CSS.
|
|
28
|
-
*/
|
|
29
|
-
function isRtl(anchor) {
|
|
30
|
-
if (!anchor || typeof window === "undefined")
|
|
31
|
-
return false;
|
|
32
|
-
const declared = anchor.closest("[dir]")?.getAttribute("dir");
|
|
33
|
-
if (declared)
|
|
34
|
-
return declared.toLowerCase() === "rtl";
|
|
35
|
-
try {
|
|
36
|
-
return window.getComputedStyle(anchor).direction === "rtl";
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
25
|
function placeFloating({ rect, align = "start", gap, flipBelow, anchor, }) {
|
|
43
|
-
const rtl =
|
|
26
|
+
const rtl = (0, direction_1.isRtlElement)(anchor);
|
|
44
27
|
const up = window.innerHeight - rect.bottom < flipBelow;
|
|
45
28
|
const style = up
|
|
46
29
|
? { bottom: window.innerHeight - rect.top + gap }
|
|
@@ -37,6 +37,7 @@ exports.AppShell = AppShell;
|
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const lucide_react_1 = require("lucide-react");
|
|
39
39
|
const React = __importStar(require("react"));
|
|
40
|
+
const core_1 = require("../../core");
|
|
40
41
|
const appshell_recipe_1 = require("./appshell.recipe");
|
|
41
42
|
/**
|
|
42
43
|
* App-shell layout with a collapsible + resizable sidebar, mirroring Attio's
|
|
@@ -62,6 +63,11 @@ function AppShell({ sidebar, children, width: widthProp, defaultWidth = 275, onW
|
|
|
62
63
|
e.preventDefault();
|
|
63
64
|
const startX = e.clientX;
|
|
64
65
|
const startW = width;
|
|
66
|
+
// The sidebar is the flex row's first child, so under RTL it renders on the
|
|
67
|
+
// RIGHT — measured. `clientX` stays physical, so pulling right must SHRINK
|
|
68
|
+
// it there. Mirroring the handle's CSS without this is the half-conversion
|
|
69
|
+
// lesson 54 is about, which is why both change in one commit (#212).
|
|
70
|
+
const dir = (0, core_1.isRtlElement)(e.currentTarget) ? -1 : 1;
|
|
65
71
|
// Attio only enters the drag state once the pointer has actually moved — a
|
|
66
72
|
// click on the handle is not a drag, so the blue bar must not flash on press.
|
|
67
73
|
let moved = false;
|
|
@@ -75,7 +81,7 @@ function AppShell({ sidebar, children, width: widthProp, defaultWidth = 275, onW
|
|
|
75
81
|
moved = true;
|
|
76
82
|
setDragging(true);
|
|
77
83
|
}
|
|
78
|
-
const w = Math.round(Math.min(maxWidth, Math.max(minWidth, startW + (ev.clientX - startX))));
|
|
84
|
+
const w = Math.round(Math.min(maxWidth, Math.max(minWidth, startW + dir * (ev.clientX - startX))));
|
|
79
85
|
setDragWidth(w);
|
|
80
86
|
last = w;
|
|
81
87
|
};
|
|
@@ -12,8 +12,8 @@ const config = (0, core_1.defineRecipe)({
|
|
|
12
12
|
display: flex; height: 100%; width: 100%; overflow: hidden; background: ${tokens_1.contract.bgSurface}; font-family: ${tokens_1.contract.font.sans}; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
13
13
|
& .appshell-side { position: relative; flex-shrink: 0; overflow: hidden; }
|
|
14
14
|
& .appshell-side-inner { height: 100%; }
|
|
15
|
-
& .appshell-resize { position: absolute; top: 0;
|
|
16
|
-
& .appshell-resize::after { content: ""; position: absolute; top: 0;
|
|
15
|
+
& .appshell-resize { position: absolute; top: 0; inset-inline-end: 0; width: 10px; height: 100%; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
16
|
+
& .appshell-resize::after { content: ""; position: absolute; top: 0; inset-inline-end: 0; width: 1px; height: 100%; background: ${tokens_1.contract.borderSubtle}; transition: background-color .16s, height .16s; }
|
|
17
17
|
& .appshell-resize:hover::after, & .appshell-resize[data-dragging]::after { width: 2px; background: ${tokens_1.contract.bgBrandSolid}; }
|
|
18
18
|
& .appshell-main { position: relative; flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
19
19
|
& .appshell-expand { position: absolute; top: ${tokens_1.contract.space["2"]}; inset-inline-start: ${tokens_1.contract.space["2"]}; z-index: 6; display: inline-flex; align-items: center; justify-content: center; width: ${tokens_1.contract.size.row}; height: ${tokens_1.contract.size.row}; padding: 0; border: none; border-radius: ${tokens_1.contract.radius.md}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; background: ${tokens_1.contract.bgSurface}; color: ${tokens_1.contract.fgMuted}; font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
@@ -42,6 +42,7 @@ const avatar_1 = require("../../components/avatar");
|
|
|
42
42
|
const checkbox_1 = require("../../components/checkbox");
|
|
43
43
|
const menu_1 = require("../../components/menu");
|
|
44
44
|
const tabs_1 = require("../../components/tabs");
|
|
45
|
+
const core_1 = require("../../core");
|
|
45
46
|
const recordstable_1 = require("../recordstable");
|
|
46
47
|
const recordpage_recipe_1 = require("./recordpage.recipe");
|
|
47
48
|
function valueEmpty(v) {
|
|
@@ -215,14 +216,20 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
215
216
|
const [resizing, setResizing] = React.useState(false);
|
|
216
217
|
const dragRef = React.useRef(null);
|
|
217
218
|
const onResizeDown = (e) => {
|
|
218
|
-
|
|
219
|
+
// details is the flex row's first child, so it sits on the right under RTL
|
|
220
|
+
// and a rightward pull must shrink it (#212)
|
|
221
|
+
dragRef.current = {
|
|
222
|
+
x: e.clientX,
|
|
223
|
+
w: detailsW,
|
|
224
|
+
dir: (0, core_1.isRtlElement)(e.currentTarget) ? -1 : 1,
|
|
225
|
+
};
|
|
219
226
|
setResizing(true);
|
|
220
227
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
221
228
|
};
|
|
222
229
|
const onResizeMove = (e) => {
|
|
223
230
|
if (!dragRef.current)
|
|
224
231
|
return;
|
|
225
|
-
const next = dragRef.current.w + (e.clientX - dragRef.current.x);
|
|
232
|
+
const next = dragRef.current.w + dragRef.current.dir * (e.clientX - dragRef.current.x);
|
|
226
233
|
setDetailsW(Math.max(300, Math.min(560, next)));
|
|
227
234
|
};
|
|
228
235
|
const onResizeUp = (e) => {
|
|
@@ -34,8 +34,8 @@ const config = (0, core_1.defineRecipe)({
|
|
|
34
34
|
|
|
35
35
|
& .recordpage-details { width: 320px; flex-shrink: 0; overflow-y: auto; border-inline-end: 1px solid ${tokens_1.contract.borderSubtle}; padding: ${tokens_1.contract.space["3"]}; }
|
|
36
36
|
& .recordpage-resizer { position: relative; flex: 0 0 auto; width: 0; align-self: stretch; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
37
|
-
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0;
|
|
38
|
-
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0;
|
|
37
|
+
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -4px; width: 9px; }
|
|
38
|
+
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -1px; width: 2px; background: transparent; transition: background-color .12s ease; }
|
|
39
39
|
& .recordpage-resizer:hover::after, & .recordpage-resizer[data-active]::after { background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 55%, transparent); }
|
|
40
40
|
& .recordpage-entity { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; padding: ${tokens_1.contract.space["1"]} ${tokens_1.contract.space["1"]} ${tokens_1.contract.space["2"]}; }
|
|
41
41
|
& .recordpage-entity-logo { width: 32px; height: 32px; border-radius: 30%; flex-shrink: 0; object-fit: cover; background: ${tokens_1.contract.bgRaised}; }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The nearest declared direction. The `dir` attribute is checked first because
|
|
3
|
+
* it is how a document says this in practice and it is readable without a
|
|
4
|
+
* layout, then computed style for a direction set purely in CSS.
|
|
5
|
+
*
|
|
6
|
+
* Shared so that a floating layer's edge choice and a drag handler's sign come
|
|
7
|
+
* from the same answer. They have to agree: mirroring one without the other is
|
|
8
|
+
* the half-conversion lesson 54 is about.
|
|
9
|
+
*/
|
|
10
|
+
export function isRtlElement(anchor) {
|
|
11
|
+
if (!anchor || typeof window === "undefined")
|
|
12
|
+
return false;
|
|
13
|
+
const declared = anchor.closest("[dir]")?.getAttribute("dir");
|
|
14
|
+
if (declared)
|
|
15
|
+
return declared.toLowerCase() === "rtl";
|
|
16
|
+
try {
|
|
17
|
+
return window.getComputedStyle(anchor).direction === "rtl";
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/esm/core/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { cx } from "./cx.js";
|
|
2
2
|
export { defineRecipe } from "./define.js";
|
|
3
|
+
export { isRtlElement } from "./direction.js";
|
|
3
4
|
export { placeFloating, } from "./place-floating.js";
|
|
4
5
|
export { Primitive } from "./primitive.js";
|
|
5
6
|
export { resolveRecipe } from "./resolve.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRtlElement } from "./direction.js";
|
|
1
2
|
/**
|
|
2
3
|
* Where a floating layer goes, in logical terms.
|
|
3
4
|
*
|
|
@@ -18,26 +19,8 @@
|
|
|
18
19
|
* be the half-conversion #146 exists to prevent — the rect it is measured
|
|
19
20
|
* against would still be physical.
|
|
20
21
|
*/
|
|
21
|
-
/**
|
|
22
|
-
* The nearest declared direction. The `dir` attribute is checked first because
|
|
23
|
-
* it is how a document says this in practice and it is readable without a
|
|
24
|
-
* layout, then computed style for a direction set purely in CSS.
|
|
25
|
-
*/
|
|
26
|
-
function isRtl(anchor) {
|
|
27
|
-
if (!anchor || typeof window === "undefined")
|
|
28
|
-
return false;
|
|
29
|
-
const declared = anchor.closest("[dir]")?.getAttribute("dir");
|
|
30
|
-
if (declared)
|
|
31
|
-
return declared.toLowerCase() === "rtl";
|
|
32
|
-
try {
|
|
33
|
-
return window.getComputedStyle(anchor).direction === "rtl";
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
22
|
export function placeFloating({ rect, align = "start", gap, flipBelow, anchor, }) {
|
|
40
|
-
const rtl =
|
|
23
|
+
const rtl = isRtlElement(anchor);
|
|
41
24
|
const up = window.innerHeight - rect.bottom < flipBelow;
|
|
42
25
|
const style = up
|
|
43
26
|
? { bottom: window.innerHeight - rect.top + gap }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PanelLeft } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { isRtlElement } from "../../core/index.js";
|
|
4
5
|
import { appshellRecipe } from "./appshell.recipe.js";
|
|
5
6
|
/**
|
|
6
7
|
* App-shell layout with a collapsible + resizable sidebar, mirroring Attio's
|
|
@@ -26,6 +27,11 @@ export function AppShell({ sidebar, children, width: widthProp, defaultWidth = 2
|
|
|
26
27
|
e.preventDefault();
|
|
27
28
|
const startX = e.clientX;
|
|
28
29
|
const startW = width;
|
|
30
|
+
// The sidebar is the flex row's first child, so under RTL it renders on the
|
|
31
|
+
// RIGHT — measured. `clientX` stays physical, so pulling right must SHRINK
|
|
32
|
+
// it there. Mirroring the handle's CSS without this is the half-conversion
|
|
33
|
+
// lesson 54 is about, which is why both change in one commit (#212).
|
|
34
|
+
const dir = isRtlElement(e.currentTarget) ? -1 : 1;
|
|
29
35
|
// Attio only enters the drag state once the pointer has actually moved — a
|
|
30
36
|
// click on the handle is not a drag, so the blue bar must not flash on press.
|
|
31
37
|
let moved = false;
|
|
@@ -39,7 +45,7 @@ export function AppShell({ sidebar, children, width: widthProp, defaultWidth = 2
|
|
|
39
45
|
moved = true;
|
|
40
46
|
setDragging(true);
|
|
41
47
|
}
|
|
42
|
-
const w = Math.round(Math.min(maxWidth, Math.max(minWidth, startW + (ev.clientX - startX))));
|
|
48
|
+
const w = Math.round(Math.min(maxWidth, Math.max(minWidth, startW + dir * (ev.clientX - startX))));
|
|
43
49
|
setDragWidth(w);
|
|
44
50
|
last = w;
|
|
45
51
|
};
|
|
@@ -9,8 +9,8 @@ const config = defineRecipe({
|
|
|
9
9
|
display: flex; height: 100%; width: 100%; overflow: hidden; background: ${c.bgSurface}; font-family: ${c.font.sans}; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
10
10
|
& .appshell-side { position: relative; flex-shrink: 0; overflow: hidden; }
|
|
11
11
|
& .appshell-side-inner { height: 100%; }
|
|
12
|
-
& .appshell-resize { position: absolute; top: 0;
|
|
13
|
-
& .appshell-resize::after { content: ""; position: absolute; top: 0;
|
|
12
|
+
& .appshell-resize { position: absolute; top: 0; inset-inline-end: 0; width: 10px; height: 100%; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
13
|
+
& .appshell-resize::after { content: ""; position: absolute; top: 0; inset-inline-end: 0; width: 1px; height: 100%; background: ${c.borderSubtle}; transition: background-color .16s, height .16s; }
|
|
14
14
|
& .appshell-resize:hover::after, & .appshell-resize[data-dragging]::after { width: 2px; background: ${c.bgBrandSolid}; }
|
|
15
15
|
& .appshell-main { position: relative; flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
16
16
|
& .appshell-expand { position: absolute; top: ${c.space["2"]}; inset-inline-start: ${c.space["2"]}; z-index: 6; display: inline-flex; align-items: center; justify-content: center; width: ${c.size.row}; height: ${c.size.row}; padding: 0; border: none; border-radius: ${c.radius.md}; box-shadow: inset 0 0 0 1px ${c.borderSubtle}; background: ${c.bgSurface}; color: ${c.fgMuted}; font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
@@ -5,6 +5,7 @@ import { Avatar } from "../../components/avatar/index.js";
|
|
|
5
5
|
import { Checkbox } from "../../components/checkbox/index.js";
|
|
6
6
|
import { Menu, MenuItem, MenuSeparator } from "../../components/menu/index.js";
|
|
7
7
|
import { Tabs } from "../../components/tabs/index.js";
|
|
8
|
+
import { isRtlElement } from "../../core/index.js";
|
|
8
9
|
import { tagTone, } from "../recordstable/index.js";
|
|
9
10
|
import { recordpageRecipe } from "./recordpage.recipe.js";
|
|
10
11
|
function valueEmpty(v) {
|
|
@@ -178,14 +179,20 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
178
179
|
const [resizing, setResizing] = React.useState(false);
|
|
179
180
|
const dragRef = React.useRef(null);
|
|
180
181
|
const onResizeDown = (e) => {
|
|
181
|
-
|
|
182
|
+
// details is the flex row's first child, so it sits on the right under RTL
|
|
183
|
+
// and a rightward pull must shrink it (#212)
|
|
184
|
+
dragRef.current = {
|
|
185
|
+
x: e.clientX,
|
|
186
|
+
w: detailsW,
|
|
187
|
+
dir: isRtlElement(e.currentTarget) ? -1 : 1,
|
|
188
|
+
};
|
|
182
189
|
setResizing(true);
|
|
183
190
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
184
191
|
};
|
|
185
192
|
const onResizeMove = (e) => {
|
|
186
193
|
if (!dragRef.current)
|
|
187
194
|
return;
|
|
188
|
-
const next = dragRef.current.w + (e.clientX - dragRef.current.x);
|
|
195
|
+
const next = dragRef.current.w + dragRef.current.dir * (e.clientX - dragRef.current.x);
|
|
189
196
|
setDetailsW(Math.max(300, Math.min(560, next)));
|
|
190
197
|
};
|
|
191
198
|
const onResizeUp = (e) => {
|
|
@@ -31,8 +31,8 @@ const config = defineRecipe({
|
|
|
31
31
|
|
|
32
32
|
& .recordpage-details { width: 320px; flex-shrink: 0; overflow-y: auto; border-inline-end: 1px solid ${c.borderSubtle}; padding: ${c.space["3"]}; }
|
|
33
33
|
& .recordpage-resizer { position: relative; flex: 0 0 auto; width: 0; align-self: stretch; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
34
|
-
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0;
|
|
35
|
-
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0;
|
|
34
|
+
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -4px; width: 9px; }
|
|
35
|
+
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -1px; width: 2px; background: transparent; transition: background-color .12s ease; }
|
|
36
36
|
& .recordpage-resizer:hover::after, & .recordpage-resizer[data-active]::after { background: color-mix(in srgb, ${c.bgBrandSolid} 55%, transparent); }
|
|
37
37
|
& .recordpage-entity { display: flex; align-items: center; gap: ${c.space["2"]}; padding: ${c.space["1"]} ${c.space["1"]} ${c.space["2"]}; }
|
|
38
38
|
& .recordpage-entity-logo { width: 32px; height: 32px; border-radius: 30%; flex-shrink: 0; object-fit: cover; background: ${c.bgRaised}; }
|
package/dist/recordpage.css
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
& .recordpage-details { width: 320px; flex-shrink: 0; overflow-y: auto; border-inline-end: 1px solid var(--border-subtle); padding: var(--space-3); }
|
|
24
24
|
& .recordpage-resizer { position: relative; flex: 0 0 auto; width: 0; align-self: stretch; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
25
|
-
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0;
|
|
26
|
-
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0;
|
|
25
|
+
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -4px; width: 9px; }
|
|
26
|
+
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -1px; width: 2px; background: transparent; transition: background-color .12s ease; }
|
|
27
27
|
& .recordpage-resizer:hover::after, & .recordpage-resizer[data-active]::after { background: color-mix(in srgb, var(--bg-brand-solid) 55%, transparent); }
|
|
28
28
|
& .recordpage-entity { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-1) var(--space-2); }
|
|
29
29
|
& .recordpage-entity-logo { width: 32px; height: 32px; border-radius: 30%; flex-shrink: 0; object-fit: cover; background: var(--bg-raised); }
|
package/dist/styles.css
CHANGED
|
@@ -1857,8 +1857,8 @@
|
|
|
1857
1857
|
display: flex; height: 100%; width: 100%; overflow: hidden; background: var(--bg-surface); font-family: var(--font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
1858
1858
|
& .appshell-side { position: relative; flex-shrink: 0; overflow: hidden; }
|
|
1859
1859
|
& .appshell-side-inner { height: 100%; }
|
|
1860
|
-
& .appshell-resize { position: absolute; top: 0;
|
|
1861
|
-
& .appshell-resize::after { content: ""; position: absolute; top: 0;
|
|
1860
|
+
& .appshell-resize { position: absolute; top: 0; inset-inline-end: 0; width: 10px; height: 100%; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
1861
|
+
& .appshell-resize::after { content: ""; position: absolute; top: 0; inset-inline-end: 0; width: 1px; height: 100%; background: var(--border-subtle); transition: background-color .16s, height .16s; }
|
|
1862
1862
|
& .appshell-resize:hover::after, & .appshell-resize[data-dragging]::after { width: 2px; background: var(--bg-brand-solid); }
|
|
1863
1863
|
& .appshell-main { position: relative; flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
1864
1864
|
& .appshell-expand { position: absolute; top: var(--space-2); inset-inline-start: var(--space-2); z-index: 6; display: inline-flex; align-items: center; justify-content: center; width: var(--size-row); height: var(--size-row); padding: 0; border: none; border-radius: var(--radius-md); box-shadow: inset 0 0 0 1px var(--border-subtle); background: var(--bg-surface); color: var(--fg-muted); font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
@@ -2213,8 +2213,8 @@
|
|
|
2213
2213
|
|
|
2214
2214
|
& .recordpage-details { width: 320px; flex-shrink: 0; overflow-y: auto; border-inline-end: 1px solid var(--border-subtle); padding: var(--space-3); }
|
|
2215
2215
|
& .recordpage-resizer { position: relative; flex: 0 0 auto; width: 0; align-self: stretch; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
2216
|
-
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0;
|
|
2217
|
-
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0;
|
|
2216
|
+
& .recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -4px; width: 9px; }
|
|
2217
|
+
& .recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -1px; width: 2px; background: transparent; transition: background-color .12s ease; }
|
|
2218
2218
|
& .recordpage-resizer:hover::after, & .recordpage-resizer[data-active]::after { background: color-mix(in srgb, var(--bg-brand-solid) 55%, transparent); }
|
|
2219
2219
|
& .recordpage-entity { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-1) var(--space-2); }
|
|
2220
2220
|
& .recordpage-entity-logo { width: 32px; height: 32px; border-radius: 30%; flex-shrink: 0; object-fit: cover; background: var(--bg-raised); }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The nearest declared direction. The `dir` attribute is checked first because
|
|
3
|
+
* it is how a document says this in practice and it is readable without a
|
|
4
|
+
* layout, then computed style for a direction set purely in CSS.
|
|
5
|
+
*
|
|
6
|
+
* Shared so that a floating layer's edge choice and a drag handler's sign come
|
|
7
|
+
* from the same answer. They have to agree: mirroring one without the other is
|
|
8
|
+
* the half-conversion lesson 54 is about.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isRtlElement(anchor: Element | null | undefined): boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { cx } from "./cx";
|
|
2
2
|
export { defineRecipe } from "./define";
|
|
3
|
+
export { isRtlElement } from "./direction";
|
|
3
4
|
export { type FloatingAlign, type PlaceFloatingOptions, placeFloating, } from "./place-floating";
|
|
4
5
|
export { Primitive } from "./primitive";
|
|
5
6
|
export { resolveRecipe } from "./resolve";
|
|
@@ -17,4 +17,24 @@ export type PlacedFloating = {
|
|
|
17
17
|
/** True when the layer was flipped above the trigger. */
|
|
18
18
|
up: boolean;
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Where a floating layer goes, in logical terms.
|
|
22
|
+
*
|
|
23
|
+
* `start` and `end` are inline edges, not left and right. `Menu` and `Popover`
|
|
24
|
+
* both took an `align` prop named that way and then wrote
|
|
25
|
+
* `align === "end" ? right : left` with no direction anywhere, so in an RTL
|
|
26
|
+
* document both were backwards and a layer near the viewport edge overflowed on
|
|
27
|
+
* the wrong side (#210).
|
|
28
|
+
*
|
|
29
|
+
* The direction is read from the anchor's computed style rather than from a
|
|
30
|
+
* prop, so an inherited `dir` on any ancestor is honoured without every call
|
|
31
|
+
* site having to thread it down.
|
|
32
|
+
*
|
|
33
|
+
* Physical `left`/`right` are deliberate in the OUTPUT: this positions a
|
|
34
|
+
* `position: fixed` layer against viewport coordinates, which are physical in
|
|
35
|
+
* both directions. What must be direction-aware is the CHOICE of edge, and
|
|
36
|
+
* that happens here, once. Converting the output to `inset-inline-start` would
|
|
37
|
+
* be the half-conversion #146 exists to prevent — the rect it is measured
|
|
38
|
+
* against would still be physical.
|
|
39
|
+
*/
|
|
20
40
|
export declare function placeFloating({ rect, align, gap, flipBelow, anchor, }: PlaceFloatingOptions): PlacedFloating;
|