@crisp-ui-kit/crisp 0.54.7 → 0.54.9
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 +36 -8
- package/dist/board.layered.css +36 -8
- 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/contextmenu/contextmenu.js +2 -2
- package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
- package/dist/cjs/components/datatable/datatable.js +590 -41
- package/dist/cjs/components/datatable/datatable.recipe.js +118 -5
- package/dist/cjs/components/dialog/dialog.js +72 -5
- package/dist/cjs/components/dialog/dialog.recipe.js +15 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +49 -5
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +365 -49
- package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +78 -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 +175 -46
- package/dist/cjs/product/board/board.recipe.js +56 -4
- package/dist/cjs/product/comments/comments.js +34 -12
- package/dist/cjs/product/comments/comments.recipe.js +31 -7
- 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/recordstable/recordstable.js +1 -17
- package/dist/cjs/product/richtext/richtext.js +76 -1
- package/dist/cjs/product/savedviews/savedviews.js +55 -24
- package/dist/cjs/product/savedviews/savedviews.recipe.js +27 -0
- 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 +50 -14
- package/dist/comments.layered.css +50 -14
- package/dist/contextmenu.css +24 -0
- package/dist/contextmenu.layered.css +24 -0
- package/dist/datatable.css +101 -37
- package/dist/datatable.layered.css +101 -37
- 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/contextmenu/contextmenu.js +2 -2
- package/dist/esm/components/contextmenu/contextmenu.recipe.js +24 -0
- package/dist/esm/components/datatable/datatable.js +590 -42
- package/dist/esm/components/datatable/datatable.recipe.js +119 -6
- package/dist/esm/components/dialog/dialog.js +72 -5
- package/dist/esm/components/dialog/dialog.recipe.js +15 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +49 -5
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +366 -50
- package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +80 -2
- 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 +176 -47
- package/dist/esm/product/board/board.recipe.js +56 -4
- package/dist/esm/product/comments/comments.js +35 -13
- package/dist/esm/product/comments/comments.recipe.js +31 -7
- 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/recordstable/recordstable.js +2 -18
- package/dist/esm/product/richtext/richtext.js +76 -1
- package/dist/esm/product/savedviews/savedviews.js +57 -26
- package/dist/esm/product/savedviews/savedviews.recipe.js +27 -0
- 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 +34 -32
- package/dist/recordstable.layered.css +34 -32
- package/dist/savedviews.css +27 -0
- package/dist/savedviews.layered.css +27 -0
- package/dist/sidebar.css +6 -1
- package/dist/sidebar.layered.css +6 -1
- package/dist/styles.css +369 -106
- package/dist/styles.layered.css +369 -106
- package/dist/tabletoolbar.css +47 -3
- package/dist/tabletoolbar.layered.css +47 -3
- 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/contextmenu/contextmenu.d.ts +8 -1
- package/dist/types/components/datatable/datatable.d.ts +98 -1
- package/dist/types/components/dialog/dialog.d.ts +25 -2
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +113 -3
- 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 +53 -6
- package/dist/types/product/comments/comments.d.ts +16 -3
- 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/recordstable/recordstable.d.ts +0 -1
- package/dist/types/product/savedviews/savedviews.d.ts +45 -8
- 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
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
.crisp-board {
|
|
2
|
-
display: flex; gap: var(--crisp-space-2); height: 100%; box-sizing: border-box;
|
|
3
|
-
|
|
2
|
+
display: flex; flex-direction: column; gap: var(--crisp-space-2); height: 100%; box-sizing: border-box; font-family: var(--crisp-font-sans); /* #718: the density toolbar's own row -- everything that used to be on
|
|
3
|
+
the root (padding/overflow/the columns themselves) moves to
|
|
4
|
+
.crisp-board-row below so this can sit above it, unscrolled. */
|
|
5
|
+
& .crisp-board-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: var(--crisp-space-2); padding: 0 var(--crisp-space-3); flex-shrink: 0; }
|
|
6
|
+
/* Measured live (Deals pipeline): a 54×28px group, 24×24px buttons,
|
|
7
|
+
2px padding/gap -- an 8px gap between the two groups comes from the
|
|
8
|
+
toolbar's own `gap` above. */
|
|
9
|
+
& .crisp-board-density { display: flex; align-items: center; gap: 2px; padding: 2px; border-radius: 10px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
10
|
+
& .crisp-board-density button { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: none; border-radius: 8px; background: none; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-family: inherit; cursor: pointer; }
|
|
11
|
+
& .crisp-board-density button[data-state="on"] { background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); }
|
|
12
|
+
& .crisp-board-row { display: flex; gap: var(--crisp-space-2); flex: 1; min-height: 0; box-sizing: border-box; padding: var(--crisp-space-3); overflow-x: auto; align-items: flex-start; }
|
|
13
|
+
& .crisp-board-col { display: flex; flex-direction: column; width: 268px; flex-shrink: 0; max-height: 100%; box-sizing: border-box; transition: width 0.15s; }
|
|
14
|
+
/* #718: a collapsed column is a 52px vertical rail -- header only (dot + count stacked above a vertical-rl title), body/footer hidden entirely. Measured live: 52px width, 8px 0 padding on the header, 14px/600 title text. */
|
|
15
|
+
& .crisp-board-col[data-density="collapse"] { width: 52px; }
|
|
16
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-header { flex-direction: column; align-items: center; height: auto; padding: var(--crisp-space-2) 0; gap: var(--crisp-space-1); }
|
|
17
|
+
/* Measured stacking order (top to bottom): dot, count, THEN the vertical title -- reordered visually via `order`, not by changing the JSX/DOM order, which stays dot/title/count (reading order, unaffected by density). */
|
|
18
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-title { order: 1; writing-mode: vertical-rl; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); }
|
|
19
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-count { order: 0; }
|
|
20
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-body, & .crisp-board-col[data-density="collapse"] .crisp-board-col-footer { display: none; }
|
|
4
21
|
& .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
22
|
& .crisp-board-col-dot { width: 12px; height: 12px; border-radius: var(--crisp-radius-full); flex-shrink: 0; }
|
|
6
23
|
& .crisp-board-col-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
|
|
7
|
-
|
|
24
|
+
/* 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
|
|
25
|
+
paints rgb(104,105,105), visibly lighter. Text now reads
|
|
26
|
+
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. */
|
|
27
|
+
& .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
28
|
& .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
29
|
& .crisp-board-col-add:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
|
|
10
30
|
& .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
31
|
& .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
32
|
& .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
33
|
& .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; }
|
|
34
|
+
& .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; }
|
|
35
|
+
/* #718: a collapsed card is a single 40px row -- measured live and independent of `c.size.tableRow`/`tableHeader` (36/40px) even though it happens to match the latter; a plain literal, not a cross-reference to a token measured for a different surface. `renderCard`'s own second argument is what actually decides what a
|
|
36
|
+
collapsed card SHOWS (Board never sees inside its opaque output);
|
|
37
|
+
this only supplies the row-shaped box that content sits in. */
|
|
38
|
+
& .crisp-board-card[data-density="collapse"] { flex-direction: row; align-items: center; height: 40px; padding: 0 var(--crisp-space-3); }
|
|
15
39
|
& .crisp-board-card:active { cursor: grabbing; }
|
|
16
40
|
& .crisp-board-card[data-dragging] { opacity: 0.4; }
|
|
17
41
|
& .crisp-board-card:focus { outline: none; }
|
|
18
42
|
& .crisp-board-card:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
|
|
43
|
+
/* #674: the pointer-events drag's own drag image -- the same card surface above, just pinned to the pointer instead of the flex flow. No shadow lift or scale: neither was measured against Attio, and this is the same card, momentarily somewhere else, not a new look. */
|
|
44
|
+
& .crisp-board-ghost { position: fixed; z-index: 1; pointer-events: none; cursor: grabbing; }
|
|
19
45
|
/* #604: a per-column calc footer, mirroring the table's own (measured
|
|
20
46
|
live, Deals pipeline). Sits below the scrollable card body, not
|
|
21
47
|
inside it or pinned to it -- Attio's own is position: static, not sticky, so it's a plain flex-shrink:0 sibling here too. */
|
|
@@ -25,11 +51,13 @@
|
|
|
25
51
|
& .crisp-board-col-calc[data-empty] { opacity: 0.7; }
|
|
26
52
|
& .crisp-board-col-calc-label { text-transform: capitalize; }
|
|
27
53
|
& .crisp-board-col-calc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
|
|
54
|
+
/* #718: "Add stage" -- measured live: 52×52px, transparent, a 1px
|
|
55
|
+
dashed border (rgb(230,231,234), exactly `borderDropzone`/`line150`
|
|
56
|
+
-- the same dashed-border colour ImportDialog's own dropzone already uses, not borderSubtle/line100, a close but different value), radius.xl (16px), a centered 16px "+". */
|
|
57
|
+
& .crisp-board-addstage { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; flex-shrink: 0; border: 1px dashed var(--crisp-border-dropzone); border-radius: var(--crisp-radius-xl); background: none; color: var(--crisp-fg-primary); font-family: inherit; cursor: pointer; }
|
|
58
|
+
& .crisp-board-addstage:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
28
59
|
/* 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
|
-
}
|
|
60
|
+
& .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
61
|
}
|
|
34
62
|
|
|
35
63
|
@media (prefers-reduced-motion: reduce) {
|
package/dist/board.layered.css
CHANGED
|
@@ -1,22 +1,48 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-board {
|
|
3
|
-
display: flex; gap: var(--crisp-space-2); height: 100%; box-sizing: border-box;
|
|
4
|
-
|
|
3
|
+
display: flex; flex-direction: column; gap: var(--crisp-space-2); height: 100%; box-sizing: border-box; font-family: var(--crisp-font-sans); /* #718: the density toolbar's own row -- everything that used to be on
|
|
4
|
+
the root (padding/overflow/the columns themselves) moves to
|
|
5
|
+
.crisp-board-row below so this can sit above it, unscrolled. */
|
|
6
|
+
& .crisp-board-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: var(--crisp-space-2); padding: 0 var(--crisp-space-3); flex-shrink: 0; }
|
|
7
|
+
/* Measured live (Deals pipeline): a 54×28px group, 24×24px buttons,
|
|
8
|
+
2px padding/gap -- an 8px gap between the two groups comes from the
|
|
9
|
+
toolbar's own `gap` above. */
|
|
10
|
+
& .crisp-board-density { display: flex; align-items: center; gap: 2px; padding: 2px; border-radius: 10px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
11
|
+
& .crisp-board-density button { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: none; border-radius: 8px; background: none; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-family: inherit; cursor: pointer; }
|
|
12
|
+
& .crisp-board-density button[data-state="on"] { background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); }
|
|
13
|
+
& .crisp-board-row { display: flex; gap: var(--crisp-space-2); flex: 1; min-height: 0; box-sizing: border-box; padding: var(--crisp-space-3); overflow-x: auto; align-items: flex-start; }
|
|
14
|
+
& .crisp-board-col { display: flex; flex-direction: column; width: 268px; flex-shrink: 0; max-height: 100%; box-sizing: border-box; transition: width 0.15s; }
|
|
15
|
+
/* #718: a collapsed column is a 52px vertical rail -- header only (dot + count stacked above a vertical-rl title), body/footer hidden entirely. Measured live: 52px width, 8px 0 padding on the header, 14px/600 title text. */
|
|
16
|
+
& .crisp-board-col[data-density="collapse"] { width: 52px; }
|
|
17
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-header { flex-direction: column; align-items: center; height: auto; padding: var(--crisp-space-2) 0; gap: var(--crisp-space-1); }
|
|
18
|
+
/* Measured stacking order (top to bottom): dot, count, THEN the vertical title -- reordered visually via `order`, not by changing the JSX/DOM order, which stays dot/title/count (reading order, unaffected by density). */
|
|
19
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-title { order: 1; writing-mode: vertical-rl; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); }
|
|
20
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-count { order: 0; }
|
|
21
|
+
& .crisp-board-col[data-density="collapse"] .crisp-board-col-body, & .crisp-board-col[data-density="collapse"] .crisp-board-col-footer { display: none; }
|
|
5
22
|
& .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
23
|
& .crisp-board-col-dot { width: 12px; height: 12px; border-radius: var(--crisp-radius-full); flex-shrink: 0; }
|
|
7
24
|
& .crisp-board-col-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
|
|
8
|
-
|
|
25
|
+
/* 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
|
|
26
|
+
paints rgb(104,105,105), visibly lighter. Text now reads
|
|
27
|
+
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. */
|
|
28
|
+
& .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
29
|
& .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
30
|
& .crisp-board-col-add:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
|
|
11
31
|
& .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
32
|
& .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
33
|
& .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
34
|
& .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; }
|
|
35
|
+
& .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; }
|
|
36
|
+
/* #718: a collapsed card is a single 40px row -- measured live and independent of `c.size.tableRow`/`tableHeader` (36/40px) even though it happens to match the latter; a plain literal, not a cross-reference to a token measured for a different surface. `renderCard`'s own second argument is what actually decides what a
|
|
37
|
+
collapsed card SHOWS (Board never sees inside its opaque output);
|
|
38
|
+
this only supplies the row-shaped box that content sits in. */
|
|
39
|
+
& .crisp-board-card[data-density="collapse"] { flex-direction: row; align-items: center; height: 40px; padding: 0 var(--crisp-space-3); }
|
|
16
40
|
& .crisp-board-card:active { cursor: grabbing; }
|
|
17
41
|
& .crisp-board-card[data-dragging] { opacity: 0.4; }
|
|
18
42
|
& .crisp-board-card:focus { outline: none; }
|
|
19
43
|
& .crisp-board-card:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
|
|
44
|
+
/* #674: the pointer-events drag's own drag image -- the same card surface above, just pinned to the pointer instead of the flex flow. No shadow lift or scale: neither was measured against Attio, and this is the same card, momentarily somewhere else, not a new look. */
|
|
45
|
+
& .crisp-board-ghost { position: fixed; z-index: 1; pointer-events: none; cursor: grabbing; }
|
|
20
46
|
/* #604: a per-column calc footer, mirroring the table's own (measured
|
|
21
47
|
live, Deals pipeline). Sits below the scrollable card body, not
|
|
22
48
|
inside it or pinned to it -- Attio's own is position: static, not sticky, so it's a plain flex-shrink:0 sibling here too. */
|
|
@@ -26,11 +52,13 @@
|
|
|
26
52
|
& .crisp-board-col-calc[data-empty] { opacity: 0.7; }
|
|
27
53
|
& .crisp-board-col-calc-label { text-transform: capitalize; }
|
|
28
54
|
& .crisp-board-col-calc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
|
|
55
|
+
/* #718: "Add stage" -- measured live: 52×52px, transparent, a 1px
|
|
56
|
+
dashed border (rgb(230,231,234), exactly `borderDropzone`/`line150`
|
|
57
|
+
-- the same dashed-border colour ImportDialog's own dropzone already uses, not borderSubtle/line100, a close but different value), radius.xl (16px), a centered 16px "+". */
|
|
58
|
+
& .crisp-board-addstage { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; flex-shrink: 0; border: 1px dashed var(--crisp-border-dropzone); border-radius: var(--crisp-radius-xl); background: none; color: var(--crisp-fg-primary); font-family: inherit; cursor: pointer; }
|
|
59
|
+
& .crisp-board-addstage:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
29
60
|
/* 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
|
-
}
|
|
61
|
+
& .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
62
|
}
|
|
35
63
|
|
|
36
64
|
@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",
|
|
@@ -45,7 +45,7 @@ const contextmenu_recipe_1 = require("./contextmenu.recipe");
|
|
|
45
45
|
* Wrap the target; right-clicking it opens the menu (flipping at the viewport
|
|
46
46
|
* edges), closing on select, outside pointer-down, and Escape.
|
|
47
47
|
*/
|
|
48
|
-
function ContextMenu({ children, items, menuLabel: menuLabelProp, className, asChild = false, }) {
|
|
48
|
+
function ContextMenu({ children, items, menuLabel: menuLabelProp, className, asChild = false, variant = "light", }) {
|
|
49
49
|
// crisp/#471: same resolution order as Breadcrumb's own.
|
|
50
50
|
const menuLabel = (0, core_1.useCrispMessage)(menuLabelProp, "Context menu");
|
|
51
51
|
const rootRef = React.useRef(null);
|
|
@@ -112,7 +112,7 @@ function ContextMenu({ children, items, menuLabel: menuLabelProp, className, asC
|
|
|
112
112
|
document.removeEventListener("keydown", onKey);
|
|
113
113
|
};
|
|
114
114
|
}, [pos, close]);
|
|
115
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [asChild ? ((0, jsx_runtime_1.jsx)(react_slot_1.Slot, { ref: rootRef, className: className, onContextMenu: open, children: children })) : ((0, jsx_runtime_1.jsx)("div", { ref: rootRef, className: (0, contextmenu_recipe_1.contextmenuRecipe)({ className }), children: (0, jsx_runtime_1.jsx)("div", { onContextMenu: open, children: children }) })), pos && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsx)("div", { ref: menuRef, className: "crisp-contextmenu-content", role: "menu", "aria-label": menuLabel, style: { left: pos.x, top: pos.y }, onKeyDown: onMenuKeyDown, children: items.map((item, i) => item.separator ? ((0, jsx_runtime_1.jsx)("hr", { className: "crisp-contextmenu-sep" }, i)) : ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", disabled: item.disabled, className: item.danger
|
|
115
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [asChild ? ((0, jsx_runtime_1.jsx)(react_slot_1.Slot, { ref: rootRef, className: className, onContextMenu: open, children: children })) : ((0, jsx_runtime_1.jsx)("div", { ref: rootRef, className: (0, contextmenu_recipe_1.contextmenuRecipe)({ className }), children: (0, jsx_runtime_1.jsx)("div", { onContextMenu: open, children: children }) })), pos && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsx)("div", { ref: menuRef, className: "crisp-contextmenu-content", "data-variant": variant === "dark" ? "dark" : undefined, role: "menu", "aria-label": menuLabel, style: { left: pos.x, top: pos.y }, onKeyDown: onMenuKeyDown, children: items.map((item, i) => item.separator ? ((0, jsx_runtime_1.jsx)("hr", { className: "crisp-contextmenu-sep" }, i)) : ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", disabled: item.disabled, className: item.danger
|
|
116
116
|
? "crisp-contextmenu-item crisp-contextmenu-item--danger"
|
|
117
117
|
: "crisp-contextmenu-item", tabIndex: -1, onClick: () => {
|
|
118
118
|
item.onSelect?.();
|
|
@@ -35,6 +35,30 @@ const config = (0, core_1.defineRecipe)({
|
|
|
35
35
|
.crisp-contextmenu-item--danger:hover { background: color-mix(in srgb, ${tokens_1.contract.bgDanger} 10%, transparent); }
|
|
36
36
|
.crisp-contextmenu-shortcut { margin-inline-start: auto; padding-inline-start: ${tokens_1.contract.space["3"]}; color: ${tokens_1.contract.fgMuted}; font-size: ${tokens_1.contract.text.sm}; }
|
|
37
37
|
.crisp-contextmenu-sep { height: 1px; margin: ${tokens_1.contract.space["1"]} 0; border: none; background: ${tokens_1.contract.borderSubtle}; }
|
|
38
|
+
/* crisp/#598: Attio's OWN cell-level context menu, measured live on
|
|
39
|
+
app.attio.com/techpartners/companies (right-click a data cell) --
|
|
40
|
+
theme-INDEPENDENT dark chrome, not this app's dark THEME (every
|
|
41
|
+
geometry value above -- 12px/8px radius, 4px/8px padding, 32px item
|
|
42
|
+
height, item gap included below -- already matched what was
|
|
43
|
+
measured here; only the colour pair differs, so only colours are
|
|
44
|
+
overridden). Same "#101112 / #eeeff1" dark-pill ink Tooltip
|
|
45
|
+
already hardcodes for the identical reason (that surface is ALSO
|
|
46
|
+
theme-independent). */
|
|
47
|
+
.crisp-contextmenu-content[data-variant="dark"] { background: #101112; box-shadow: none; }
|
|
48
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item { gap: ${tokens_1.contract.space["1.5"]}; color: #eeeff1; }
|
|
49
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item svg { color: #898a8d; }
|
|
50
|
+
/* Plain-item hover re-measured live on the same cell menu (right-click
|
|
51
|
+
"Description" on app.attio.com/techpartners/companies, hover the
|
|
52
|
+
"Copy" row): getComputedStyle on the row is rgba(255,255,255,0.1),
|
|
53
|
+
not the 0.08 this used to guess at. The danger row's own hover
|
|
54
|
+
(hovering "Clear value") still didn't get re-measured -- that one
|
|
55
|
+
remains the derived red-on-black translation below, flagged on
|
|
56
|
+
crisp#598 for someone to confirm live. */
|
|
57
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item:hover { background: rgba(255,255,255,0.1); }
|
|
58
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger { color: #ff5454; }
|
|
59
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger svg { color: #ff5454; }
|
|
60
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger:hover { background: rgba(255,84,84,0.12); }
|
|
61
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-sep { background: rgba(255,255,255,0.1); }
|
|
38
62
|
`,
|
|
39
63
|
variants: {},
|
|
40
64
|
defaultVariants: {},
|