@crisp-ui-kit/crisp 0.54.6 → 0.54.8
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/authshell.css +5 -1
- package/dist/authshell.layered.css +5 -1
- package/dist/badge.css +5 -5
- package/dist/badge.layered.css +5 -5
- package/dist/board.css +13 -9
- package/dist/board.layered.css +13 -9
- package/dist/button.css +3 -1
- package/dist/button.layered.css +3 -1
- package/dist/cjs/components/badge/badge.recipe.js +28 -5
- package/dist/cjs/components/button/button.js +33 -2
- package/dist/cjs/components/button/button.recipe.js +31 -5
- package/dist/cjs/components/datatable/datatable.js +66 -7
- package/dist/cjs/components/datatable/datatable.recipe.js +57 -2
- package/dist/cjs/components/dialog/dialog.js +8 -5
- package/dist/cjs/components/dialog/dialog.recipe.js +15 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +19 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +239 -50
- package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +44 -0
- package/dist/cjs/components/tabs/tabs.recipe.js +11 -2
- package/dist/cjs/components/tooltip/tooltip.js +23 -9
- package/dist/cjs/components/tooltip/tooltip.recipe.js +10 -9
- package/dist/cjs/product/authshell/authshell.js +14 -1
- package/dist/cjs/product/authshell/authshell.recipe.js +13 -1
- package/dist/cjs/product/board/board.js +122 -27
- package/dist/cjs/product/board/board.recipe.js +14 -2
- package/dist/cjs/product/comments/comments.js +23 -10
- package/dist/cjs/product/comments/comments.recipe.js +17 -3
- package/dist/cjs/product/formdialog/formdialog.js +10 -4
- package/dist/cjs/product/navitem/navitem.js +7 -2
- package/dist/cjs/product/navitem/navitem.recipe.js +16 -0
- package/dist/cjs/product/objectsettings/index.js +1 -2
- package/dist/cjs/product/objectsettings/objectsettings.js +3 -13
- package/dist/cjs/product/richtext/richtext.js +76 -1
- package/dist/cjs/product/sidebar/sidebar.js +68 -15
- package/dist/cjs/product/sidebar/sidebar.recipe.js +6 -1
- package/dist/cjs/tokens/palette.js +32 -1
- package/dist/cjs/tokens/semantic.js +11 -0
- package/dist/comments.css +36 -10
- package/dist/comments.layered.css +36 -10
- package/dist/datatable.css +47 -9
- package/dist/datatable.layered.css +47 -9
- package/dist/dialog.css +13 -1
- package/dist/dialog.layered.css +13 -1
- package/dist/esm/components/badge/badge.recipe.js +28 -5
- package/dist/esm/components/button/button.js +34 -3
- package/dist/esm/components/button/button.recipe.js +31 -5
- package/dist/esm/components/datatable/datatable.js +67 -8
- package/dist/esm/components/datatable/datatable.recipe.js +57 -2
- package/dist/esm/components/dialog/dialog.js +8 -5
- package/dist/esm/components/dialog/dialog.recipe.js +15 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +19 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +240 -51
- package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +44 -0
- package/dist/esm/components/tabs/tabs.recipe.js +11 -2
- package/dist/esm/components/tooltip/tooltip.js +23 -9
- package/dist/esm/components/tooltip/tooltip.recipe.js +10 -9
- package/dist/esm/product/authshell/authshell.js +15 -2
- package/dist/esm/product/authshell/authshell.recipe.js +13 -1
- package/dist/esm/product/authshell/index.js +1 -1
- package/dist/esm/product/board/board.js +122 -27
- package/dist/esm/product/board/board.recipe.js +14 -2
- package/dist/esm/product/comments/comments.js +23 -10
- package/dist/esm/product/comments/comments.recipe.js +17 -3
- package/dist/esm/product/formdialog/formdialog.js +10 -4
- package/dist/esm/product/navitem/index.js +1 -1
- package/dist/esm/product/navitem/navitem.js +7 -2
- package/dist/esm/product/navitem/navitem.recipe.js +16 -0
- package/dist/esm/product/objectsettings/index.js +1 -1
- package/dist/esm/product/objectsettings/objectsettings.js +2 -12
- package/dist/esm/product/richtext/richtext.js +76 -1
- package/dist/esm/product/sidebar/sidebar.js +69 -16
- package/dist/esm/product/sidebar/sidebar.recipe.js +6 -1
- package/dist/esm/tokens/palette.js +32 -1
- package/dist/esm/tokens/semantic.js +11 -0
- package/dist/native/tokens.d.ts +1 -1
- package/dist/native/tokens.js +16 -2
- package/dist/navitem.css +7 -0
- package/dist/navitem.layered.css +7 -0
- package/dist/recordstable.css +2 -1
- package/dist/recordstable.layered.css +2 -1
- package/dist/sidebar.css +6 -1
- package/dist/sidebar.layered.css +6 -1
- package/dist/styles.css +165 -42
- package/dist/styles.layered.css +165 -42
- package/dist/tabletoolbar.css +17 -1
- package/dist/tabletoolbar.layered.css +17 -1
- package/dist/tabs.css +1 -1
- package/dist/tabs.layered.css +1 -1
- package/dist/tokens.css +9 -1
- package/dist/tokens.json +16 -2
- package/dist/tooltip.css +1 -1
- package/dist/tooltip.layered.css +1 -1
- package/dist/types/components/badge/badge.recipe.d.ts +5 -1
- package/dist/types/components/button/button.recipe.d.ts +18 -5
- package/dist/types/components/datatable/datatable.d.ts +9 -0
- package/dist/types/components/dialog/dialog.d.ts +25 -2
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +47 -5
- package/dist/types/components/tooltip/tooltip.d.ts +12 -4
- package/dist/types/components/tooltip/tooltip.recipe.d.ts +1 -1
- package/dist/types/product/authshell/authshell.d.ts +26 -2
- package/dist/types/product/authshell/index.d.ts +1 -1
- package/dist/types/product/board/board.d.ts +5 -4
- package/dist/types/product/comments/comments.d.ts +10 -2
- package/dist/types/product/formdialog/formdialog.d.ts +2 -1
- package/dist/types/product/navitem/index.d.ts +1 -1
- package/dist/types/product/navitem/navitem.d.ts +23 -0
- package/dist/types/product/objectsettings/index.d.ts +1 -1
- package/dist/types/product/objectsettings/objectsettings.d.ts +0 -10
- package/dist/types/product/sidebar/sidebar.d.ts +33 -5
- package/dist/types/tokens/contract.d.ts +4 -0
- package/dist/types/tokens/palette.d.ts +13 -1
- package/dist/types/tokens/semantic.d.ts +11 -0
- package/package.json +1 -1
package/dist/authshell.css
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
&[data-single-pane] { justify-content: center; }
|
|
4
4
|
& .crisp-authshell-progress { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: var(--crisp-border-default); }
|
|
5
5
|
& .crisp-authshell-progress-fill { height: 2px; background: var(--crisp-fg-primary); border-radius: 4px; transition: width .2s; }
|
|
6
|
-
& .crisp-authshell-logo { margin-top: var(--crisp-space-8); height: 24px; display: flex; align-items: center; }
|
|
6
|
+
& .crisp-authshell-logo { margin-top: var(--crisp-space-8); min-height: 24px; display: flex; align-items: center; gap: var(--crisp-space-2-5); }
|
|
7
|
+
& .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-brand-solid); color: #fff; display: flex; align-items: center; justify-content: center; }
|
|
8
|
+
& .crisp-authshell-logo-text { display: flex; flex-direction: column; line-height: 1.2; }
|
|
9
|
+
& .crisp-authshell-logo-title { font-size: var(--crisp-text-xl); font-weight: var(--crisp-weight-bold); letter-spacing: -0.2px; color: var(--crisp-fg-primary); }
|
|
10
|
+
& .crisp-authshell-logo-subtitle { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
|
|
7
11
|
& .crisp-authshell-card { margin-top: var(--crisp-space-6); display: flex; height: 692px; border-radius: 20px; border: 1px solid var(--crisp-border-subtle); overflow: hidden; }
|
|
8
12
|
&[data-single-pane] .crisp-authshell-card { height: auto; border: none; border-radius: 0; overflow: visible; }
|
|
9
13
|
& .crisp-authshell-pane { flex: 0 0 564px; padding: 72px; display: flex; flex-direction: column; box-sizing: border-box; }
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
&[data-single-pane] { justify-content: center; }
|
|
5
5
|
& .crisp-authshell-progress { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: var(--crisp-border-default); }
|
|
6
6
|
& .crisp-authshell-progress-fill { height: 2px; background: var(--crisp-fg-primary); border-radius: 4px; transition: width .2s; }
|
|
7
|
-
& .crisp-authshell-logo { margin-top: var(--crisp-space-8); height: 24px; display: flex; align-items: center; }
|
|
7
|
+
& .crisp-authshell-logo { margin-top: var(--crisp-space-8); min-height: 24px; display: flex; align-items: center; gap: var(--crisp-space-2-5); }
|
|
8
|
+
& .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-brand-solid); color: #fff; display: flex; align-items: center; justify-content: center; }
|
|
9
|
+
& .crisp-authshell-logo-text { display: flex; flex-direction: column; line-height: 1.2; }
|
|
10
|
+
& .crisp-authshell-logo-title { font-size: var(--crisp-text-xl); font-weight: var(--crisp-weight-bold); letter-spacing: -0.2px; color: var(--crisp-fg-primary); }
|
|
11
|
+
& .crisp-authshell-logo-subtitle { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
|
|
8
12
|
& .crisp-authshell-card { margin-top: var(--crisp-space-6); display: flex; height: 692px; border-radius: 20px; border: 1px solid var(--crisp-border-subtle); overflow: hidden; }
|
|
9
13
|
&[data-single-pane] .crisp-authshell-card { height: auto; border: none; border-radius: 0; overflow: visible; }
|
|
10
14
|
& .crisp-authshell-pane { flex: 0 0 564px; padding: 72px; display: flex; flex-direction: column; box-sizing: border-box; }
|
package/dist/badge.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
.crisp-badge {
|
|
2
|
-
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing:
|
|
2
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: normal; white-space: nowrap;
|
|
3
3
|
& .crisp-badge-dot { width: 6px; height: 6px; flex-shrink: 0; border-radius: var(--crisp-radius-full); background: currentColor; }
|
|
4
4
|
}
|
|
5
5
|
.crisp-badge--tone_neutral {
|
|
6
|
-
background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-soft); box-shadow: inset 0 0 0 1px var(--crisp-border-
|
|
6
|
+
background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-soft); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle);
|
|
7
7
|
}
|
|
8
8
|
.crisp-badge--tone_brand {
|
|
9
9
|
background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: var(--crisp-fg-brand-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent);
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
background: var(--crisp-bg-purple-soft); color: var(--crisp-fg-purple-soft); box-shadow: inset 0 0 0 1px var(--crisp-ring-purple-soft);
|
|
22
22
|
}
|
|
23
23
|
.crisp-badge--size_small {
|
|
24
|
-
height: 16px; padding-inline: var(--crisp-space-1
|
|
24
|
+
height: 16px; padding-inline: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; border-radius: var(--crisp-radius-sm);
|
|
25
25
|
}
|
|
26
26
|
.crisp-badge--size_medium {
|
|
27
|
-
height: 20px; padding-inline: var(--crisp-space-
|
|
27
|
+
height: 20px; padding-inline: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; gap: 3px; border-radius: var(--crisp-radius-sm);
|
|
28
28
|
}
|
|
29
29
|
.crisp-badge--size_large {
|
|
30
|
-
height: var(--crisp-size-chip); padding-inline: 9px; font-size: var(--crisp-text-lg); border-radius: var(--crisp-radius-chip);
|
|
30
|
+
height: var(--crisp-size-chip); padding-inline: 9px; font-size: var(--crisp-text-lg); letter-spacing: -0.1px; border-radius: var(--crisp-radius-chip);
|
|
31
31
|
}
|
|
32
32
|
.crisp-badge--variant_soft {
|
|
33
33
|
|
package/dist/badge.layered.css
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-badge {
|
|
3
|
-
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing:
|
|
3
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: normal; white-space: nowrap;
|
|
4
4
|
& .crisp-badge-dot { width: 6px; height: 6px; flex-shrink: 0; border-radius: var(--crisp-radius-full); background: currentColor; }
|
|
5
5
|
}
|
|
6
6
|
.crisp-badge--tone_neutral {
|
|
7
|
-
background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-soft); box-shadow: inset 0 0 0 1px var(--crisp-border-
|
|
7
|
+
background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-soft); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle);
|
|
8
8
|
}
|
|
9
9
|
.crisp-badge--tone_brand {
|
|
10
10
|
background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: var(--crisp-fg-brand-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent);
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
background: var(--crisp-bg-purple-soft); color: var(--crisp-fg-purple-soft); box-shadow: inset 0 0 0 1px var(--crisp-ring-purple-soft);
|
|
23
23
|
}
|
|
24
24
|
.crisp-badge--size_small {
|
|
25
|
-
height: 16px; padding-inline: var(--crisp-space-1
|
|
25
|
+
height: 16px; padding-inline: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; border-radius: var(--crisp-radius-sm);
|
|
26
26
|
}
|
|
27
27
|
.crisp-badge--size_medium {
|
|
28
|
-
height: 20px; padding-inline: var(--crisp-space-
|
|
28
|
+
height: 20px; padding-inline: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; gap: 3px; border-radius: var(--crisp-radius-sm);
|
|
29
29
|
}
|
|
30
30
|
.crisp-badge--size_large {
|
|
31
|
-
height: var(--crisp-size-chip); padding-inline: 9px; font-size: var(--crisp-text-lg); border-radius: var(--crisp-radius-chip);
|
|
31
|
+
height: var(--crisp-size-chip); padding-inline: 9px; font-size: var(--crisp-text-lg); letter-spacing: -0.1px; border-radius: var(--crisp-radius-chip);
|
|
32
32
|
}
|
|
33
33
|
.crisp-badge--variant_soft {
|
|
34
34
|
|
package/dist/board.css
CHANGED
|
@@ -4,21 +4,28 @@
|
|
|
4
4
|
& .crisp-board-col-header { display: flex; align-items: center; gap: var(--crisp-space-2); height: 36px; padding: 0 var(--crisp-space-1); flex-shrink: 0; }
|
|
5
5
|
& .crisp-board-col-dot { width: 12px; height: 12px; border-radius: var(--crisp-radius-full); flex-shrink: 0; }
|
|
6
6
|
& .crisp-board-col-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
|
|
7
|
-
|
|
7
|
+
/* crisp#697: HANDOFF's #472 note claimed the count pill's color-mix(fgPrimary, 63%, transparent) reproduced the measured rgb(80,81,85) -- it doesn't; 63% of fgPrimary composited on white
|
|
8
|
+
paints rgb(104,105,105), visibly lighter. Text now reads
|
|
9
|
+
fgNeutralSoft directly (the same rgb(80,81,85) token Badge's neutral tone uses, #696), and the ring the pill was also missing (inset 0 0 0 1px borderSubtle, measured rgb(238,239,241)) is added. */
|
|
10
|
+
& .crisp-board-col-count { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); line-height: 16px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
|
|
8
11
|
& .crisp-board-col-add { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin-inline-start: auto; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
|
|
9
12
|
& .crisp-board-col-add:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
|
|
10
13
|
& .crisp-board-col-body { display: flex; flex-direction: column; gap: var(--crisp-space-2); flex: 1; min-height: 48px; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); overflow-y: auto; transition: background 0.12s; }
|
|
11
14
|
& .crisp-board-col[data-over] .crisp-board-col-body { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 7%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent); }
|
|
12
15
|
& .crisp-board-newcard { display: flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); height: 32px; flex-shrink: 0; border: none; border-radius: var(--crisp-radius-md); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; }
|
|
13
16
|
& .crisp-board-newcard:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); color: var(--crisp-fg-primary); }
|
|
14
|
-
& .crisp-board-card { display: flex; flex-direction: column; gap: var(--crisp-space-2); box-sizing: border-box; padding: var(--crisp-space-2-5) var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); cursor: grab; }
|
|
17
|
+
& .crisp-board-card { display: flex; flex-direction: column; gap: var(--crisp-space-2); box-sizing: border-box; padding: var(--crisp-space-2-5) var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); cursor: grab; touch-action: none; }
|
|
15
18
|
& .crisp-board-card:active { cursor: grabbing; }
|
|
16
19
|
& .crisp-board-card[data-dragging] { opacity: 0.4; }
|
|
17
20
|
& .crisp-board-card:focus { outline: none; }
|
|
18
21
|
& .crisp-board-card:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
|
|
19
|
-
/* #
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
/* #674: the pointer-events drag's own drag image -- the same card
|
|
23
|
+
surface above, just pinned to the pointer instead of the flex flow.
|
|
24
|
+
No shadow lift or scale: neither was measured against Attio, and this
|
|
25
|
+
is the same card, momentarily somewhere else, not a new look. */
|
|
26
|
+
& .crisp-board-ghost { position: fixed; z-index: 1; pointer-events: none; cursor: grabbing; }
|
|
27
|
+
/* #604: a per-column calc footer, mirroring the table's own (measured live, Deals pipeline). Sits below the scrollable card body, not inside it or pinned to it -- Attio's own is position: static, not
|
|
28
|
+
sticky, so it's a plain flex-shrink:0 sibling here too. */
|
|
22
29
|
& .crisp-board-col-footer { flex-shrink: 0; padding: var(--crisp-space-1) 0; }
|
|
23
30
|
& .crisp-board-col-calc { display: flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); width: 100%; height: 28px; padding: var(--crisp-space-1) var(--crisp-space-2); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: none; font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); cursor: pointer; }
|
|
24
31
|
& .crisp-board-col-calc:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
@@ -26,10 +33,7 @@
|
|
|
26
33
|
& .crisp-board-col-calc-label { text-transform: capitalize; }
|
|
27
34
|
& .crisp-board-col-calc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
|
|
28
35
|
/* announced, never drawn */
|
|
29
|
-
& .crisp-board-live {
|
|
30
|
-
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
31
|
-
overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
|
|
32
|
-
}
|
|
36
|
+
& .crisp-board-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
@media (prefers-reduced-motion: reduce) {
|
package/dist/board.layered.css
CHANGED
|
@@ -5,21 +5,28 @@
|
|
|
5
5
|
& .crisp-board-col-header { display: flex; align-items: center; gap: var(--crisp-space-2); height: 36px; padding: 0 var(--crisp-space-1); flex-shrink: 0; }
|
|
6
6
|
& .crisp-board-col-dot { width: 12px; height: 12px; border-radius: var(--crisp-radius-full); flex-shrink: 0; }
|
|
7
7
|
& .crisp-board-col-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
|
|
8
|
-
|
|
8
|
+
/* crisp#697: HANDOFF's #472 note claimed the count pill's color-mix(fgPrimary, 63%, transparent) reproduced the measured rgb(80,81,85) -- it doesn't; 63% of fgPrimary composited on white
|
|
9
|
+
paints rgb(104,105,105), visibly lighter. Text now reads
|
|
10
|
+
fgNeutralSoft directly (the same rgb(80,81,85) token Badge's neutral tone uses, #696), and the ring the pill was also missing (inset 0 0 0 1px borderSubtle, measured rgb(238,239,241)) is added. */
|
|
11
|
+
& .crisp-board-col-count { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); line-height: 16px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
|
|
9
12
|
& .crisp-board-col-add { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin-inline-start: auto; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
|
|
10
13
|
& .crisp-board-col-add:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
|
|
11
14
|
& .crisp-board-col-body { display: flex; flex-direction: column; gap: var(--crisp-space-2); flex: 1; min-height: 48px; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); overflow-y: auto; transition: background 0.12s; }
|
|
12
15
|
& .crisp-board-col[data-over] .crisp-board-col-body { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 7%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent); }
|
|
13
16
|
& .crisp-board-newcard { display: flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); height: 32px; flex-shrink: 0; border: none; border-radius: var(--crisp-radius-md); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; }
|
|
14
17
|
& .crisp-board-newcard:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); color: var(--crisp-fg-primary); }
|
|
15
|
-
& .crisp-board-card { display: flex; flex-direction: column; gap: var(--crisp-space-2); box-sizing: border-box; padding: var(--crisp-space-2-5) var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); cursor: grab; }
|
|
18
|
+
& .crisp-board-card { display: flex; flex-direction: column; gap: var(--crisp-space-2); box-sizing: border-box; padding: var(--crisp-space-2-5) var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); cursor: grab; touch-action: none; }
|
|
16
19
|
& .crisp-board-card:active { cursor: grabbing; }
|
|
17
20
|
& .crisp-board-card[data-dragging] { opacity: 0.4; }
|
|
18
21
|
& .crisp-board-card:focus { outline: none; }
|
|
19
22
|
& .crisp-board-card:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
|
|
20
|
-
/* #
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
/* #674: the pointer-events drag's own drag image -- the same card
|
|
24
|
+
surface above, just pinned to the pointer instead of the flex flow.
|
|
25
|
+
No shadow lift or scale: neither was measured against Attio, and this
|
|
26
|
+
is the same card, momentarily somewhere else, not a new look. */
|
|
27
|
+
& .crisp-board-ghost { position: fixed; z-index: 1; pointer-events: none; cursor: grabbing; }
|
|
28
|
+
/* #604: a per-column calc footer, mirroring the table's own (measured live, Deals pipeline). Sits below the scrollable card body, not inside it or pinned to it -- Attio's own is position: static, not
|
|
29
|
+
sticky, so it's a plain flex-shrink:0 sibling here too. */
|
|
23
30
|
& .crisp-board-col-footer { flex-shrink: 0; padding: var(--crisp-space-1) 0; }
|
|
24
31
|
& .crisp-board-col-calc { display: flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); width: 100%; height: 28px; padding: var(--crisp-space-1) var(--crisp-space-2); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: none; font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); cursor: pointer; }
|
|
25
32
|
& .crisp-board-col-calc:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
@@ -27,10 +34,7 @@
|
|
|
27
34
|
& .crisp-board-col-calc-label { text-transform: capitalize; }
|
|
28
35
|
& .crisp-board-col-calc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
|
|
29
36
|
/* announced, never drawn */
|
|
30
|
-
& .crisp-board-live {
|
|
31
|
-
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
32
|
-
overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
|
|
33
|
-
}
|
|
37
|
+
& .crisp-board-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
@media (prefers-reduced-motion: reduce) {
|
package/dist/button.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.crisp-button {
|
|
2
|
-
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.
|
|
2
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.07px; line-height: 20px; white-space: nowrap; border: none; cursor: pointer; transition: background-color .2s, box-shadow .2s, color .2s;
|
|
3
3
|
&:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
4
4
|
}
|
|
5
5
|
.crisp-button--intent_primary {
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
}
|
|
26
26
|
.crisp-button--size_md {
|
|
27
27
|
height: 28px; padding-inline: var(--crisp-space-2); font-size: var(--crisp-text-lg); border-radius: var(--crisp-radius-md);
|
|
28
|
+
&[data-icon-start] { padding-inline-start: var(--crisp-space-1-5); }
|
|
29
|
+
&[data-icon-end] { padding-inline-end: var(--crisp-space-1-5); }
|
|
28
30
|
}
|
|
29
31
|
.crisp-button--size_lg {
|
|
30
32
|
height: 40px; padding-inline: 14px; font-size: var(--crisp-text-lg); border-radius: 10px;
|
package/dist/button.layered.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-button {
|
|
3
|
-
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.
|
|
3
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.07px; line-height: 20px; white-space: nowrap; border: none; cursor: pointer; transition: background-color .2s, box-shadow .2s, color .2s;
|
|
4
4
|
&:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
5
5
|
}
|
|
6
6
|
.crisp-button--intent_primary {
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
}
|
|
27
27
|
.crisp-button--size_md {
|
|
28
28
|
height: 28px; padding-inline: var(--crisp-space-2); font-size: var(--crisp-text-lg); border-radius: var(--crisp-radius-md);
|
|
29
|
+
&[data-icon-start] { padding-inline-start: var(--crisp-space-1-5); }
|
|
30
|
+
&[data-icon-end] { padding-inline-end: var(--crisp-space-1-5); }
|
|
29
31
|
}
|
|
30
32
|
.crisp-button--size_lg {
|
|
31
33
|
height: 40px; padding-inline: 14px; font-size: var(--crisp-text-lg); border-radius: 10px;
|
|
@@ -14,7 +14,11 @@ const spec = (0, core_1.defineSpec)({
|
|
|
14
14
|
gap: tokens_1.contract.space["1"],
|
|
15
15
|
fontFamily: tokens_1.contract.font.sans,
|
|
16
16
|
fontWeight: tokens_1.contract.weight.medium,
|
|
17
|
-
|
|
17
|
+
// crisp#696: every Attio pill measured this round -- the count pill and
|
|
18
|
+
// the tag pill both -- reads `normal` tracking, not the `-0.1px` this
|
|
19
|
+
// used to carry as a base default. `large` (unmeasured this round) keeps
|
|
20
|
+
// the old `-0.1px` as its own variant override, below.
|
|
21
|
+
letterSpacing: "normal",
|
|
18
22
|
whiteSpace: "nowrap",
|
|
19
23
|
},
|
|
20
24
|
parts: [
|
|
@@ -41,7 +45,12 @@ const spec = (0, core_1.defineSpec)({
|
|
|
41
45
|
neutral: {
|
|
42
46
|
background: tokens_1.contract.bgRaised,
|
|
43
47
|
color: tokens_1.contract.fgNeutralSoft,
|
|
44
|
-
|
|
48
|
+
// crisp#696: Attio's neutral pill ring measures rgb(238,239,241) --
|
|
49
|
+
// borderSubtle/line100 (#eeeff1), not borderDefault/line200
|
|
50
|
+
// (#e4e4e7). Background is unchanged: Attio measured
|
|
51
|
+
// rgb(246,247,247) against crisp's bgRaised #f4f4f5, a 2-unit
|
|
52
|
+
// difference the `savedviews` recipe already treats as noise.
|
|
53
|
+
boxShadow: `inset 0 0 0 1px ${tokens_1.contract.borderSubtle}`,
|
|
45
54
|
},
|
|
46
55
|
brand: {
|
|
47
56
|
background: soft(tokens_1.contract.bgBrandSolid, 14),
|
|
@@ -75,22 +84,36 @@ const spec = (0, core_1.defineSpec)({
|
|
|
75
84
|
},
|
|
76
85
|
},
|
|
77
86
|
size: {
|
|
87
|
+
// crisp#696: Attio's count pill (sidebar "Tasks 1", the "+1" overflow)
|
|
88
|
+
// measures 16px h / 4px padding / 12px text / 16px line-height -- was
|
|
89
|
+
// 6px padding, 11px text, and no line-height at all.
|
|
78
90
|
small: {
|
|
79
91
|
height: "16px",
|
|
80
|
-
paddingInline: tokens_1.contract.space["1
|
|
81
|
-
fontSize: tokens_1.contract.text.
|
|
92
|
+
paddingInline: tokens_1.contract.space["1"],
|
|
93
|
+
fontSize: tokens_1.contract.text.sm,
|
|
94
|
+
lineHeight: "16px",
|
|
82
95
|
borderRadius: tokens_1.contract.radius.sm,
|
|
83
96
|
},
|
|
97
|
+
// crisp#696: Attio's tag pill (Standard/System/Enriched) measures
|
|
98
|
+
// 20px h / 4px padding / 12px text / 16px line-height / 3px gap -- was
|
|
99
|
+
// 8px padding, no line-height, and the base's (now normal-tracking)
|
|
100
|
+
// 4px gap.
|
|
84
101
|
medium: {
|
|
85
102
|
height: "20px",
|
|
86
|
-
paddingInline: tokens_1.contract.space["
|
|
103
|
+
paddingInline: tokens_1.contract.space["1"],
|
|
87
104
|
fontSize: tokens_1.contract.text.sm,
|
|
105
|
+
lineHeight: "16px",
|
|
106
|
+
gap: "3px",
|
|
88
107
|
borderRadius: tokens_1.contract.radius.sm,
|
|
89
108
|
},
|
|
109
|
+
// crisp#696: not re-measured this round -- kept exactly as it rendered
|
|
110
|
+
// before the base tracking changed to `normal`, by pinning the old
|
|
111
|
+
// `-0.1px` here instead. Gap is untouched (base's 4px).
|
|
90
112
|
large: {
|
|
91
113
|
height: tokens_1.contract.size.chip,
|
|
92
114
|
paddingInline: "9px",
|
|
93
115
|
fontSize: tokens_1.contract.text.lg,
|
|
116
|
+
letterSpacing: "-0.1px",
|
|
94
117
|
borderRadius: tokens_1.contract.radius.chip,
|
|
95
118
|
},
|
|
96
119
|
},
|
|
@@ -42,10 +42,41 @@ const button_recipe_1 = require("./button.recipe");
|
|
|
42
42
|
function Spinner() {
|
|
43
43
|
return ((0, jsx_runtime_1.jsxs)("svg", { width: "1em", height: "1em", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", style: { flexShrink: 0 }, children: [(0, jsx_runtime_1.jsx)("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeOpacity: "0.25", strokeWidth: "2" }), (0, jsx_runtime_1.jsx)("path", { d: "M8 1.5a6.5 6.5 0 0 1 6.5 6.5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: (0, jsx_runtime_1.jsx)("animateTransform", { attributeName: "transform", type: "rotate", from: "0 8 8", to: "360 8 8", dur: "0.7s", repeatCount: "indefinite" }) })] }));
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* crisp#682: an "icon" part is a valid element whose type is the host `svg`
|
|
47
|
+
* tag or isn't a string at all (a lucide icon, `Spinner`, any component) --
|
|
48
|
+
* everything else (strings, numbers, host elements like `span`) is a "text"
|
|
49
|
+
* part.
|
|
50
|
+
*/
|
|
51
|
+
function isIconPart(node) {
|
|
52
|
+
return (React.isValidElement(node) &&
|
|
53
|
+
(node.type === "svg" || typeof node.type !== "string"));
|
|
54
|
+
}
|
|
45
55
|
exports.Button = React.forwardRef(({ intent, size, shape, touchTarget, className, asChild, loading, disabled, children, type, ...props }, ref) => {
|
|
46
56
|
const isDisabled = disabled || loading;
|
|
47
57
|
// Slot (asChild) requires a single child — never inject the spinner in that mode.
|
|
48
|
-
|
|
58
|
+
// An array (not a Fragment) so React.Children.toArray below sees the
|
|
59
|
+
// Spinner as its own top-level part instead of one opaque Fragment node.
|
|
60
|
+
const content = loading && !asChild
|
|
61
|
+
? [(0, jsx_runtime_1.jsx)(Spinner, {}, "crisp-button-spinner"), children]
|
|
62
|
+
: children;
|
|
63
|
+
// crisp#682: md's icon-side padding (6px vs 8px) depends on whether a
|
|
64
|
+
// side ends in an icon or text, which CSS alone can't tell apart
|
|
65
|
+
// (`<svg/>Label` vs `Label<svg/>` both skip text nodes under
|
|
66
|
+
// `:first-child`). Skipped for `asChild`, which renders someone else's
|
|
67
|
+
// single element rather than a list of parts.
|
|
68
|
+
let iconStart;
|
|
69
|
+
let iconEnd;
|
|
70
|
+
if (!asChild) {
|
|
71
|
+
const parts = React.Children.toArray(content);
|
|
72
|
+
const hasText = parts.some((part) => !isIconPart(part));
|
|
73
|
+
if (hasText && parts.length > 0) {
|
|
74
|
+
if (isIconPart(parts[0]))
|
|
75
|
+
iconStart = true;
|
|
76
|
+
if (isIconPart(parts[parts.length - 1]))
|
|
77
|
+
iconEnd = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
49
80
|
return ((0, jsx_runtime_1.jsx)(core_1.Primitive.button, { ref: ref, asChild: asChild, className: (0, button_recipe_1.buttonRecipe)({
|
|
50
81
|
intent,
|
|
51
82
|
size,
|
|
@@ -57,6 +88,6 @@ exports.Button = React.forwardRef(({ intent, size, shape, touchTarget, className
|
|
|
57
88
|
// a design system should submit a form by accident, so default to
|
|
58
89
|
// "button" and make submitting explicit. `asChild` renders someone
|
|
59
90
|
// else's element, which must not be given a button-only attribute.
|
|
60
|
-
type: asChild ? type : (type ?? "button"), disabled: isDisabled, "aria-busy": loading || undefined, "data-loading": loading ? "" : undefined, ...props, children: content }));
|
|
91
|
+
type: asChild ? type : (type ?? "button"), disabled: isDisabled, "aria-busy": loading || undefined, "data-loading": loading ? "" : undefined, "data-icon-start": iconStart ? "" : undefined, "data-icon-end": iconEnd ? "" : undefined, ...props, children: content }));
|
|
61
92
|
});
|
|
62
93
|
exports.Button.displayName = "Button";
|
|
@@ -12,7 +12,11 @@ const spec = (0, core_1.defineSpec)({
|
|
|
12
12
|
gap: tokens_1.contract.space["1.5"],
|
|
13
13
|
fontFamily: tokens_1.contract.font.sans,
|
|
14
14
|
fontWeight: tokens_1.contract.weight.medium,
|
|
15
|
-
|
|
15
|
+
// crisp#682: every 14px button label on app.attio.com measures -0.07px
|
|
16
|
+
// (17 of 17 on the Companies view, plus 222 sidebar/nav labels).
|
|
17
|
+
// -0.14px is Attio's table-cell tracking, not its button tracking --
|
|
18
|
+
// that's where this value came from.
|
|
19
|
+
letterSpacing: "-0.07px",
|
|
16
20
|
lineHeight: "20px",
|
|
17
21
|
whiteSpace: "nowrap",
|
|
18
22
|
border: "none",
|
|
@@ -80,6 +84,9 @@ const spec = (0, core_1.defineSpec)({
|
|
|
80
84
|
},
|
|
81
85
|
},
|
|
82
86
|
size: {
|
|
87
|
+
// sm/lg unchanged by crisp#682 -- neither was measured against Attio
|
|
88
|
+
// (Attio's small button is 20px, not sm's 24px; see crisp#682), so
|
|
89
|
+
// only md's icon-side padding is touched here.
|
|
83
90
|
sm: {
|
|
84
91
|
height: "24px",
|
|
85
92
|
paddingInline: tokens_1.contract.space["1.5"],
|
|
@@ -87,10 +94,29 @@ const spec = (0, core_1.defineSpec)({
|
|
|
87
94
|
borderRadius: tokens_1.contract.radius.md,
|
|
88
95
|
},
|
|
89
96
|
md: {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
decls: {
|
|
98
|
+
height: "28px",
|
|
99
|
+
paddingInline: tokens_1.contract.space["2"],
|
|
100
|
+
fontSize: tokens_1.contract.text.lg,
|
|
101
|
+
borderRadius: tokens_1.contract.radius.md,
|
|
102
|
+
},
|
|
103
|
+
// crisp#682: a side that ends in an icon gets 6px padding instead of
|
|
104
|
+
// 8px. CSS can't tell `<svg/>Label` apart from `Label<svg/>`
|
|
105
|
+
// (`:first-child` skips text nodes), so `Button` marks the icon
|
|
106
|
+
// sides itself via `data-icon-start`/`data-icon-end` (button.tsx).
|
|
107
|
+
// The attribute selector outranks the plain class above it, so this
|
|
108
|
+
// always wins over the shorthand `paddingInline` regardless of
|
|
109
|
+
// declaration order.
|
|
110
|
+
parts: [
|
|
111
|
+
{
|
|
112
|
+
selector: "&[data-icon-start]",
|
|
113
|
+
decls: { paddingInlineStart: tokens_1.contract.space["1.5"] },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
selector: "&[data-icon-end]",
|
|
117
|
+
decls: { paddingInlineEnd: tokens_1.contract.space["1.5"] },
|
|
118
|
+
},
|
|
119
|
+
],
|
|
94
120
|
},
|
|
95
121
|
lg: {
|
|
96
122
|
height: "40px",
|
|
@@ -529,6 +529,34 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
529
529
|
const showColumn = React.useCallback((key) => applyColumns(allColumns.map((c) => (c.key === key ? { ...c, hidden: false } : c))), [allColumns, applyColumns]);
|
|
530
530
|
const [selCell, setSelCell] = React.useState(null);
|
|
531
531
|
const gridRef = React.useRef(null);
|
|
532
|
+
// #579: the click-to-expand overlay for a truncated cell. Only one is ever
|
|
533
|
+
// open (a second click-to-expand elsewhere closes the first, matching "one
|
|
534
|
+
// live session"-style singular UI state already used for `selCell`).
|
|
535
|
+
const [expandedCell, setExpandedCell] = React.useState(null);
|
|
536
|
+
// Closes on any click outside the overlay itself (a click ON it is
|
|
537
|
+
// stopped from propagating, see the overlay's own onClick) and on
|
|
538
|
+
// Escape, kept for accessibility even though Attio's own Escape handling
|
|
539
|
+
// wasn't reliable when this was measured live (crisp#579).
|
|
540
|
+
React.useEffect(() => {
|
|
541
|
+
if (!expandedCell)
|
|
542
|
+
return;
|
|
543
|
+
const close = (e) => {
|
|
544
|
+
const target = e.target;
|
|
545
|
+
if (target?.closest?.(".crisp-datatable-cell-overlay"))
|
|
546
|
+
return;
|
|
547
|
+
setExpandedCell(null);
|
|
548
|
+
};
|
|
549
|
+
const onKey = (e) => {
|
|
550
|
+
if (e.key === "Escape")
|
|
551
|
+
setExpandedCell(null);
|
|
552
|
+
};
|
|
553
|
+
document.addEventListener("click", close, true);
|
|
554
|
+
document.addEventListener("keydown", onKey);
|
|
555
|
+
return () => {
|
|
556
|
+
document.removeEventListener("click", close, true);
|
|
557
|
+
document.removeEventListener("keydown", onKey);
|
|
558
|
+
};
|
|
559
|
+
}, [expandedCell]);
|
|
532
560
|
const sort = sortProp !== undefined ? sortProp : internalSort;
|
|
533
561
|
// One shape downstream: [] when unsorted, ordered by precedence otherwise.
|
|
534
562
|
const sorts = React.useMemo(() => (sort == null ? [] : Array.isArray(sort) ? sort : [sort]), [sort]);
|
|
@@ -957,6 +985,10 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
957
985
|
function openEditing(id, key) {
|
|
958
986
|
setEditError(null);
|
|
959
987
|
setEditing({ id, key });
|
|
988
|
+
// #579: the editing branch pre-empts the plain-cell render (and its
|
|
989
|
+
// overlay) entirely, but `expandedCell` itself would survive a later
|
|
990
|
+
// cancel-back-to-plain-view unless cleared here too.
|
|
991
|
+
setExpandedCell(null);
|
|
960
992
|
}
|
|
961
993
|
// Async-aware (#385): a sync `onEdit` (returns undefined) commits exactly
|
|
962
994
|
// as before -- close the editor immediately. A Promise-returning `onEdit`
|
|
@@ -1212,7 +1244,17 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
1212
1244
|
// aria-sort is specified as one header per table, and with the
|
|
1213
1245
|
// multi-sort the `sort` prop allows, a second "ascending" says
|
|
1214
1246
|
// nothing about which column came first.
|
|
1215
|
-
|
|
1247
|
+
// crisp/#605: the primary/entity column is inert -- no menu,
|
|
1248
|
+
// no click-to-sort, no aria-sort claim -- matching Attio's
|
|
1249
|
+
// own measured behaviour exactly ("no popover opens, no
|
|
1250
|
+
// right-click menu either... primary column is fixed by
|
|
1251
|
+
// construction, not a menu target", checklist G-023). A
|
|
1252
|
+
// consumer setting `sortable`/`columnMenu` on an entity
|
|
1253
|
+
// column doesn't get a sort affordance there; this is
|
|
1254
|
+
// `!isPrimary` gating every interactive branch below, not
|
|
1255
|
+
// just the menu.
|
|
1256
|
+
const isPrimary = col.type === "entity";
|
|
1257
|
+
const sortable = !isPrimary && (col.sortable || !!columnMenu);
|
|
1216
1258
|
const ariaSort = !sortable
|
|
1217
1259
|
? undefined
|
|
1218
1260
|
: sorts[0]?.key === col.key
|
|
@@ -1233,9 +1275,9 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
1233
1275
|
}
|
|
1234
1276
|
else if (e.key === "Escape")
|
|
1235
1277
|
setRenaming(null);
|
|
1236
|
-
} })) : columnMenu ? ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "start", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-datatable-sort", "data-sorted": sortDirByKey.get(col.key), "aria-label": `${typeof col.header === "string"
|
|
1278
|
+
} })) : columnMenu && !isPrimary ? ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "start", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-datatable-sort", "data-sorted": sortDirByKey.get(col.key), "aria-label": `${typeof col.header === "string"
|
|
1237
1279
|
? col.header
|
|
1238
|
-
: col.key} ${crispMessages["column options"] ?? "column options"}`, children: inner }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setSort({ key: col.key, dir: "asc" }), children: sortAscendingLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setSort({ key: col.key, dir: "desc" }), children: sortDescendingLabel }), col.managed === false ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => moveColumn(col.key, -1), children: moveColumnLeftLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => moveColumn(col.key, 1), children: moveColumnRightLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setRenaming(col.key), children: editColumnLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => hideColumn(col.key), children: hideColumnLabel })] }))] })) : col.sortable ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-datatable-sort", "data-sorted": sortDirByKey.get(col.key), onClick: () => cycleSort(col.key), children: inner })) : (inner), col.resizable !== false && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-resize", "data-resizing": resizingKey === col.key ? "" : undefined, onPointerDown: (e) => startResize(col, e), onPointerMove: moveResize, onPointerUp: endResize }))] }, col.key));
|
|
1280
|
+
: col.key} ${crispMessages["column options"] ?? "column options"}`, children: inner }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setSort({ key: col.key, dir: "asc" }), children: sortAscendingLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setSort({ key: col.key, dir: "desc" }), children: sortDescendingLabel }), col.managed === false ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => moveColumn(col.key, -1), children: moveColumnLeftLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => moveColumn(col.key, 1), children: moveColumnRightLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setRenaming(col.key), children: editColumnLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => hideColumn(col.key), children: hideColumnLabel })] }))] })) : col.sortable && !isPrimary ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-datatable-sort", "data-sorted": sortDirByKey.get(col.key), onClick: () => cycleSort(col.key), children: inner })) : (inner), col.readOnly && ((0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "crisp-datatable-readonly", size: 12, strokeWidth: 1.75, "aria-hidden": "true" })), col.resizable !== false && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-resize", "data-resizing": resizingKey === col.key ? "" : undefined, onPointerDown: (e) => startResize(col, e), onPointerMove: moveResize, onPointerUp: endResize }))] }, col.key));
|
|
1239
1281
|
})] }) }), (0, jsx_runtime_1.jsx)(table_1.TableBody, { children: error ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-empty", children: error }) }) })) : rows.length === 0 ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-empty", children: empty }) }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [topSpacerHeight > 0 && ((0, jsx_runtime_1.jsx)("tr", { "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("td", { colSpan: totalCols, style: {
|
|
1240
1282
|
height: topSpacerHeight,
|
|
1241
1283
|
padding: 0,
|
|
@@ -1337,22 +1379,39 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
1337
1379
|
commitEdit(row.id, col.key, e.currentTarget.value);
|
|
1338
1380
|
} }), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }, col.key));
|
|
1339
1381
|
}
|
|
1382
|
+
const rendered = col.render
|
|
1383
|
+
? col.render(value, row)
|
|
1384
|
+
: renderTyped(col, value, avatarTone, row, col.format?.(value, row), locale);
|
|
1385
|
+
const isExpanded = expandedCell?.id === row.id &&
|
|
1386
|
+
expandedCell?.key === col.key;
|
|
1340
1387
|
return ((0, jsx_runtime_1.jsxs)(table_1.TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, className: col.editable
|
|
1341
1388
|
? "crisp-datatable-editable"
|
|
1342
1389
|
: undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
|
|
1343
1390
|
? ""
|
|
1344
|
-
: undefined, onClick: () => {
|
|
1391
|
+
: undefined, "data-cell-expanded": isExpanded ? "" : undefined, onClick: (e) => {
|
|
1345
1392
|
// the keyboard model's anchor -- a single click
|
|
1346
1393
|
// only selects. A genuine double-click (or Enter
|
|
1347
1394
|
// on the selected cell) opens the editor (#411),
|
|
1348
1395
|
// matching RecordsTable's own onDoubleClick.
|
|
1349
1396
|
setSelCell({ id: row.id, key: col.key });
|
|
1350
1397
|
gridRef.current?.focus({ preventScroll: true });
|
|
1398
|
+
// #579: a truncated cell also opens a read-only
|
|
1399
|
+
// overlay with its full value -- settled live
|
|
1400
|
+
// that this is click, not Attio's usual
|
|
1401
|
+
// hover-expand (a 1s hover on a truncated
|
|
1402
|
+
// Description cell produced nothing). Measured
|
|
1403
|
+
// the same way as `ThLabel`'s header tooltip
|
|
1404
|
+
// (#534): scrollWidth vs clientWidth on the
|
|
1405
|
+
// element the CSS ellipsis actually applies to,
|
|
1406
|
+
// which for a data cell is the `td` itself
|
|
1407
|
+
// (table.recipe.ts's own `overflow: hidden;
|
|
1408
|
+
// text-overflow: ellipsis`), not a nested span.
|
|
1409
|
+
setExpandedCell(isLabelTruncated(e.currentTarget)
|
|
1410
|
+
? { id: row.id, key: col.key }
|
|
1411
|
+
: null);
|
|
1351
1412
|
}, onDoubleClick: col.editable
|
|
1352
1413
|
? () => openEditing(row.id, col.key)
|
|
1353
|
-
: undefined, children: [col.
|
|
1354
|
-
? col.render(value, row)
|
|
1355
|
-
: renderTyped(col, value, avatarTone, row, col.format?.(value, row), locale), errorHere && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1414
|
+
: undefined, children: [!isExpanded && rendered, errorHere && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-edit-error", children: errorHere })), isExpanded && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-cell-overlay", onClick: (e) => e.stopPropagation(), children: rendered }))] }, col.key));
|
|
1356
1415
|
})] }, row.id));
|
|
1357
1416
|
}), bottomSpacerHeight > 0 && ((0, jsx_runtime_1.jsx)("tr", { "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("td", { colSpan: totalCols, style: {
|
|
1358
1417
|
height: bottomSpacerHeight,
|