@crisp-ui-kit/crisp 0.55.0 → 0.55.2
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/badge.css +1 -1
- package/dist/badge.layered.css +1 -1
- package/dist/cjs/components/badge/badge.recipe.js +10 -5
- package/dist/cjs/components/datatable/countries.js +73 -0
- package/dist/cjs/components/datatable/datatable.js +282 -12
- package/dist/cjs/components/datatable/datatable.recipe.js +51 -1
- package/dist/datatable.css +172 -102
- package/dist/datatable.layered.css +172 -102
- package/dist/esm/components/badge/badge.recipe.js +10 -5
- package/dist/esm/components/datatable/countries.js +68 -0
- package/dist/esm/components/datatable/datatable.js +283 -13
- package/dist/esm/components/datatable/datatable.recipe.js +51 -1
- package/dist/styles.css +173 -103
- package/dist/styles.layered.css +173 -103
- package/dist/types/components/badge/badge.recipe.d.ts +3 -1
- package/dist/types/components/datatable/countries.d.ts +12 -0
- package/dist/types/components/datatable/datatable.d.ts +47 -3
- package/package.json +1 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-datatable {
|
|
3
|
-
display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans);
|
|
4
|
-
|
|
3
|
+
display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans); /* The toolbar carried no horizontal inset of its own, so its content (search box, sort/filter chips) sat flush at the table's outer edge
|
|
4
|
+
(x=0) while the header row's own th/td padding (table.recipe.ts, #465) starts every column's text at 12px -- one row down, the SAME
|
|
5
|
+
grid's own header cell already begins at that inset. c.space["3"] here just matches the row directly below it, measured live against Attio (the sort/filter row's search chip lines up with the first
|
|
6
|
+
column header's text, not the grid's raw edge). */
|
|
7
|
+
& .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: 49px; border-top: 1px solid var(--crisp-border-subtle); padding: 0 var(--crisp-space-3); }
|
|
5
8
|
& .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
|
|
6
9
|
& .crisp-datatable-search svg { position: absolute; inset-inline-start: var(--crisp-space-2); color: var(--crisp-fg-muted); pointer-events: none; }
|
|
7
10
|
& .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-6); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s; }
|
|
@@ -9,72 +12,83 @@
|
|
|
9
12
|
& .crisp-datatable-search input:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
10
13
|
& .crisp-datatable-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
11
14
|
/* announced, never drawn */
|
|
12
|
-
& .crisp-datatable-live {
|
|
13
|
-
/* crisp/#645: visually-hidden-but-focusable, not visually-hidden-and- inert like .crisp-datatable-live above -- same clip-rect shape, this one just needs to actually hold DOM focus so a native copy/paste event has something to fire on (see its own comment in datatable.tsx). resize: none keeps a stray native textarea handle from ever being drawable, even though it's clipped to 1px anyway. */
|
|
14
|
-
& .crisp-datatable-clipboard-proxy {
|
|
15
|
+
& .crisp-datatable-live {
|
|
15
16
|
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
16
17
|
overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
|
|
17
|
-
resize: none;
|
|
18
18
|
}
|
|
19
|
-
/*
|
|
20
|
-
|
|
19
|
+
/* crisp/#645: visually-hidden-but-focusable, not visually-hidden-and-
|
|
20
|
+
inert like .crisp-datatable-live above -- same clip-rect shape,
|
|
21
|
+
this one just needs to actually hold DOM focus so a native
|
|
22
|
+
copy/paste event has something to fire on (see its own comment
|
|
23
|
+
in datatable.tsx). resize: none keeps a stray native textarea handle
|
|
24
|
+
from ever being drawable, even though it's clipped to 1px anyway. */
|
|
25
|
+
& .crisp-datatable-clipboard-proxy { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; resize: none; }
|
|
26
|
+
/* single active cell (crisp/#645 follow-up -- this had no styling of its own at all before, a gap against Golden Rule 0: RecordsTable's own
|
|
27
|
+
measured value, .crisp-recordstable-td[data-selected], brought
|
|
28
|
+
across unchanged now that a visible anchor actually matters for the
|
|
29
|
+
Shift+Click range below). */
|
|
21
30
|
& td[data-selected] { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; border-radius: var(--crisp-radius-sm); z-index: 3; }
|
|
22
|
-
/* crisp/#645, G-101: a Shift+Click range -- reuses the row-selection
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
/* crisp/#645, G-101: a Shift+Click range -- reuses the row-selection
|
|
32
|
+
tint formula already used twice below (tbody tr[data-selected] td)
|
|
33
|
+
for the same reason a selected group of rows gets one: a selected
|
|
34
|
+
GROUP of cells is the same kind of state, just cell-scoped instead of
|
|
35
|
+
row-scoped. Not independently re-measured against Attio's own range highlight (out of scope this slice -- flagged on crisp#645). */
|
|
36
|
+
& td[data-in-range] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
|
|
37
|
+
/* The header cell has no padding of its own -- the button owns the full cell so the whole header is clickable (its own inset is 12px). Mirrors the summary-row fix. `& .crisp-table th`, not bare `& th` (found while re-checking #691's
|
|
26
38
|
own fix live): DataTable's root nests `Table`'s own wrapper, whose
|
|
27
|
-
`table.recipe.ts` carries `& th, & td { padding: 0 12px }
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
specificity tie to source order again. */
|
|
39
|
+
`table.recipe.ts` carries `& th, & td { padding: 0 12px }` at the
|
|
40
|
+
SAME specificity (.crisp-table th vs .crisp-datatable th, both one
|
|
41
|
+
class + one element) -- a bare `& th` here only won by source
|
|
42
|
+
order, which flipped the moment table.recipe.ts's own CSS file happened to land after datatable's in the build (alphabetical:
|
|
43
|
+
datatable < table), silently reinstating 12px on every header cell
|
|
44
|
+
in the whole product and making every interactive column's button double it to 24px. Scoping through `.crisp-table` adds a second class to the selector (0,2,1 beats 0,1,1) so this can never lose a specificity tie to source order again. */
|
|
33
45
|
& .crisp-table th { padding: 0; }
|
|
34
|
-
/* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger):
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
/* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger):
|
|
47
|
+
Menu wraps its trigger in a flex div that sizes to its content, so a
|
|
48
|
+
long header grew past the column instead of filling it -- with nothing
|
|
49
|
+
left to squeeze, the label's own ellipsis CSS (below) had no properly -sized box to truncate against, and the th's own overflow hard-clipped
|
|
50
|
+
it with no "...". Fill the wrapper, scoped to th so Menu's default (correct everywhere else) is untouched. */
|
|
37
51
|
& th .crisp-menu { width: 100%; height: 100%; }
|
|
38
52
|
& .crisp-datatable-th-inner { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; }
|
|
39
53
|
& .crisp-datatable-th-inner > svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
|
|
40
|
-
/* crisp/#605 follow-up: a header with neither a Menu nor a sort button
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
/* crisp/#605 follow-up: a header with neither a Menu nor a sort button (the primary/entity column's now-inert path, or any column with no
|
|
55
|
+
columnMenu/sortable at all) renders .crisp-datatable-th-inner as a
|
|
56
|
+
DIRECT child of th instead of nested inside a button -- and loses
|
|
57
|
+
the button's own 12px inset, drifting left of the td's 12px base
|
|
58
|
+
inset. Measured live on Attio's own Companies grid: the header label and the cell's own text land at the exact same x (335px from the
|
|
59
|
+
viewport, both 40px from the column's left edge) via two different padding+icon+gap combinations -- Attio does not leave the inert primary header flush left either. The `>` combinator targets only the button-less case: Menu's own wrapper (`.crisp-menu`) and the
|
|
60
|
+
plain sort `button` both put >=1 more level between `th` and this
|
|
61
|
+
span, so their own 12px (already on the button) isn't doubled. */
|
|
46
62
|
& th > .crisp-datatable-th-inner { box-sizing: border-box; padding: 0 var(--crisp-space-3); }
|
|
47
|
-
/* The summary cell has NO padding of its own -- the calculation trigger
|
|
48
|
-
owns the whole 246x37 area so a click anywhere in the cell opens the
|
|
49
|
-
menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
|
|
63
|
+
/* The summary cell has NO padding of its own -- the calculation trigger owns the whole 246x37 area so a click anywhere in the cell opens the menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
|
|
50
64
|
& tfoot td { padding: 0; }
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
/* Group heading row -- a view is grouped under a subtle label with the
|
|
66
|
+
bucket's count beside it. */
|
|
67
|
+
& .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
53
68
|
& .crisp-datatable-viewsettings:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
54
69
|
& .crisp-datatable-group-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
|
|
55
|
-
|
|
70
|
+
& .crisp-datatable-group { height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-3); background: var(--crisp-bg-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
|
|
56
71
|
& .crisp-datatable-group-count { margin-inline-start: var(--crisp-space-1-5); color: var(--crisp-fg-muted); font-variant-numeric: tabular-nums; }
|
|
57
72
|
& .crisp-datatable-th-rename { width: 100%; min-width: 0; height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-1); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); outline: none; }
|
|
58
73
|
& .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
59
74
|
& .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
60
|
-
/* #465: 12px per the comment above (and Attio's measured header cell
|
|
75
|
+
/* #465: 12px per the comment above (and Attio's measured header cell
|
|
76
|
+
padding: 0 12px) -- was still c.space["2"] (8px), drifted from its
|
|
77
|
+
own documented value. */
|
|
61
78
|
& .crisp-datatable-sort { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
62
|
-
/* A right-aligned column's header keeps its field-icon + label packed at
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end of a flex-start row with no override needed -- that's what pins the trailing icon on Attio too. */
|
|
79
|
+
/* A right-aligned column's header keeps its field-icon + label packed at the START, matching its own values only in that the VALUES right-align -- Attio's own right-aligned header (measured live: "Twitter follower
|
|
80
|
+
count") keeps the icon+label left and only the trailing column-menu
|
|
81
|
+
icon sits pinned to the right edge, independent of the label's width (#406, reversing #393's own justify-content: flex-end here). The
|
|
82
|
+
chevron's own margin-inline-start: auto below already pins IT to the end of a flex-start row with no override needed -- that's what pins
|
|
83
|
+
the trailing icon on Attio too. */
|
|
68
84
|
& .crisp-datatable-chevron { margin-inline-start: auto; flex-shrink: 0; color: var(--crisp-fg-muted); opacity: 0; transition: opacity .12s; }
|
|
69
85
|
& .crisp-datatable-sort:hover .crisp-datatable-chevron { opacity: 0.6; }
|
|
70
86
|
& .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
|
|
71
87
|
/* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
|
|
72
88
|
& th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: var(--crisp-bg-surface); }
|
|
73
|
-
& th[data-sticky] { z-index: 3;
|
|
74
|
-
|
|
75
|
-
`tfoot td { z-index: 2 }
|
|
76
|
-
`), which `td[data-sticky]`'s own higher-specificity
|
|
77
|
-
`z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
|
|
89
|
+
& th[data-sticky] { z-index: 3; }
|
|
90
|
+
/* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
|
|
91
|
+
`tfoot td { z-index: 2 }`), which `td[data-sticky]`'s own higher-specificity `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
|
|
78
92
|
& tfoot td[data-sticky] { z-index: 2; }
|
|
79
93
|
& tbody tr[data-selected] td[data-sticky] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
|
|
80
94
|
& tbody tr:hover td[data-sticky] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
|
|
@@ -91,88 +105,98 @@
|
|
|
91
105
|
& tbody tr[data-selected] td { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
|
|
92
106
|
& tbody tr[data-selected]:hover td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface))); }
|
|
93
107
|
& .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
|
|
94
|
-
/* crisp/#758: AI-enriched column marker, ported from RecordsTable's own
|
|
95
|
-
|
|
108
|
+
/* crisp/#758: AI-enriched column marker, ported from RecordsTable's own
|
|
109
|
+
already-measured value -- a full-bleed #9b69ff (bgPurple) wash at 4%,
|
|
110
|
+
composited OVER whatever row-state background the cell already has
|
|
111
|
+
(hover, selected), which is why it's an ::after overlay and not a plain background. */
|
|
96
112
|
& td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, var(--crisp-bg-purple) 4%, transparent); }
|
|
97
113
|
& .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
|
|
98
114
|
& .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
99
|
-
/* An async onEdit's in-flight state (#385) -- the field/picker dims and
|
|
115
|
+
/* An async onEdit's in-flight state (#385) -- the field/picker dims and
|
|
116
|
+
stops accepting input rather than disappearing, so the just-typed or
|
|
117
|
+
just-chosen value stays visible while its commit is pending. Same
|
|
118
|
+
treatment Input already gives its own disabled state. */
|
|
100
119
|
& .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
101
120
|
& .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
|
|
102
|
-
/* The rejection's message, beside the cell's own content -- a sibling in
|
|
103
|
-
hover-only wrapper (a Tooltip, say) around a custom render can't hide
|
|
104
|
-
|
|
121
|
+
/* The rejection's message, beside the cell's own content -- a sibling in
|
|
122
|
+
the TableCell, never nested inside a column's own render output, so a hover-only wrapper (a Tooltip, say) around a custom render can't hide
|
|
123
|
+
it. Block-level: the row grows to fit it rather than floating over
|
|
124
|
+
the row below, so it can never end up clipped by the scroll
|
|
125
|
+
container's overflow the way a position:fixed escape would need guarding against. */
|
|
105
126
|
& .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
|
|
106
|
-
/* editor: "select"/"switch" -- always live, no separate edit mode, so
|
|
107
|
-
|
|
108
|
-
RecordsTable's own --picker cells (measured there already; DataTable shares RecordsTable's grid, Golden Rule 0). */
|
|
127
|
+
/* editor: "select"/"switch" -- always live, no separate edit mode, so the cell IS the picker: same chromeless-fill treatment as RecordsTable's own --picker cells (measured there already; DataTable
|
|
128
|
+
shares RecordsTable's grid, Golden Rule 0). */
|
|
109
129
|
& .crisp-datatable-td--picker { padding: 0; }
|
|
110
130
|
& .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
|
|
111
131
|
& .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
|
|
112
132
|
& .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
|
|
113
|
-
/* reference picker: a leading 16px avatar per option, plus a trailing
|
|
114
|
-
"Create X" row when the consumer supplies one (#449) -- same measured
|
|
115
|
-
values as RecordsTable's own .crisp-recordstable-refoption*. */
|
|
133
|
+
/* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- same measured values as RecordsTable's own .crisp-recordstable-refoption*. */
|
|
116
134
|
& .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
117
135
|
& .crisp-datatable-refoption-avatar { flex-shrink: 0; }
|
|
118
136
|
& .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
|
|
119
137
|
& .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
|
|
120
|
-
/* #579: click-to-expand overlay for a truncated cell -- a read-only,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for
|
|
138
|
+
/* #579: click-to-expand overlay for a truncated cell -- a read-only,
|
|
139
|
+
full-value card, settled live as click (not Attio's usual hover-expand) and measured position: absolute, unlike this file's
|
|
140
|
+
floating chrome (Menu/Select popovers) which portals and escapes the
|
|
141
|
+
scroll container. Deliberately NOT the edit-error pattern above
|
|
142
|
+
(grow the row, block-level, never clipped): a virtualized grid
|
|
143
|
+
assumes a fixed row height for its scroll math (owned by the grid
|
|
144
|
+
core, not this cell), so growing a row here would fight it. Anchored
|
|
145
|
+
to the SAME column width the cell itself renders at (measured:
|
|
146
|
+
Attio's own overlay came out only slightly wider than the triggering cell's clientWidth -- the overshoot is this padding),
|
|
147
|
+
wrapping onto as many lines as the value needs; it CAN be clipped by
|
|
148
|
+
`.crisp-table-scroll`'s own `overflow: auto` if it grows past the visible viewport, same as the edit-error comment above already notes for any position:absolute escape -- unmeasured against Attio for this exact case, flagged rather than guessed. */
|
|
126
149
|
& td[data-cell-expanded] { overflow: visible; z-index: 7; }
|
|
127
|
-
& .crisp-datatable-cell-overlay {
|
|
128
|
-
position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box;
|
|
129
|
-
padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface);
|
|
130
|
-
border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px;
|
|
131
|
-
font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium);
|
|
132
|
-
color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default;
|
|
133
|
-
}
|
|
150
|
+
& .crisp-datatable-cell-overlay { position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box; padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface); border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default; }
|
|
134
151
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
135
152
|
& table { table-layout: fixed; }
|
|
136
|
-
/* read-only/computed column marker (#596) -- sits just inside the
|
|
137
|
-
resize handle, same absolute-positioned-against-the-th approach the
|
|
138
|
-
handle itself uses, since th has zero padding and the button/inner
|
|
139
|
-
row fills the whole cell with no room left for a trailing sibling. */
|
|
153
|
+
/* read-only/computed column marker (#596) -- sits just inside the resize handle, same absolute-positioned-against-the-th approach the handle itself uses, since th has zero padding and the button/inner row fills the whole cell with no room left for a trailing sibling. */
|
|
140
154
|
& .crisp-datatable-readonly { position: absolute; top: 50%; inset-inline-end: 11px; transform: translateY(-50%); color: var(--crisp-fg-muted); pointer-events: none; z-index: 5; }
|
|
141
155
|
/* column resize grip */
|
|
142
156
|
& .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
143
157
|
& .crisp-datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
|
|
144
158
|
& .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
|
|
145
|
-
/* crisp/#731 -- column drag-to-reorder. Measured live against Attio's
|
|
146
|
-
opacity and the plain `pointer` cursor throughout (Attio draws no
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
line, a 3px bar in the same measured brand blue the resize handle's own hover state (above) already uses, positioned on whichever edge of the hovered header the pointer is closer to. */
|
|
159
|
+
/* crisp/#731 -- column drag-to-reorder. Measured live against Attio's
|
|
160
|
+
own grid (the issue's own comment): the dragged header keeps full opacity and the plain `pointer` cursor throughout (Attio draws no ghost and no "grabbing" cursor of its own), so this adds nothing to `[data-dragging]` itself -- the ONLY new paint is the drop-indicator line, a 3px bar in the same measured brand blue the resize handle's
|
|
161
|
+
own hover state (above) already uses, positioned on whichever edge of
|
|
162
|
+
the hovered header the pointer is closer to. */
|
|
150
163
|
& th[data-dragging] { cursor: pointer; }
|
|
151
|
-
& th[data-drop-before]::before, & th[data-drop-after]::after {
|
|
164
|
+
& th[data-drop-before]::before, & th[data-drop-after]::after {
|
|
165
|
+
content: ""; position: absolute; top: 0; width: 3px; height: 100%;
|
|
166
|
+
background: var(--crisp-bg-brand-solid); z-index: 6; pointer-events: none;
|
|
167
|
+
}
|
|
152
168
|
& th[data-drop-before]::before { inset-inline-start: -1.5px; }
|
|
153
169
|
& th[data-drop-after]::after { inset-inline-end: -1.5px; }
|
|
154
170
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
155
171
|
& .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
|
|
156
|
-
/* crisp/#601 supersession follow-up -- the same flex-row shape
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
/* crisp/#601 supersession follow-up -- the same flex-row shape
|
|
173
|
+
.crisp-datatable-entity gives its own avatar+name+actions, but
|
|
174
|
+
structural only (no entity-specific colour/weight): lets
|
|
175
|
+
onOpenComments/onOpenPreview compose alongside ANY column's own content (a custom render, or a non-entity built-in type), not just type: "entity"'s own inline rendering. */
|
|
159
176
|
& .crisp-datatable-cell-actions-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; width: 100%; }
|
|
160
|
-
/* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text,
|
|
161
|
-
hover reveal -- identical at rest and on hover: text-decoration-color
|
|
162
|
-
|
|
177
|
+
/* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text,
|
|
178
|
+
plain hover vs rest) that Attio's underline is unconditional, not a hover reveal -- identical at rest and on hover: text-decoration-color is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text
|
|
179
|
+
colour. The previous :hover-gated, full-contrast rule here was itself
|
|
180
|
+
unmeasured and wrong in two ways at once. */
|
|
163
181
|
& .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
|
|
164
182
|
& button.crisp-datatable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
|
|
165
|
-
/* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
|
|
166
|
-
|
|
167
|
-
not just the icons' own hit target -- still reveals them) or
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
/* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
|
|
184
|
+
column, revealed on ROW hover (the issue's own title says "row
|
|
185
|
+
hover", and a live check confirmed hovering well past the name -- not just the icons' own hit target -- still reveals them) or
|
|
186
|
+
keyboard focus. Measured live: border-radius 6px, icon colour
|
|
187
|
+
rgba(0,0,0,0.55) at rest, darkening to the full text colour with a
|
|
188
|
+
rgba(0,0,0,0.04) background on the icon's OWN hover -- both expressed as color-mix against fgPrimary so they stay correct in dark mode instead of a literal black that would invert. */
|
|
170
189
|
& .crisp-datatable-entity-actions { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
|
|
171
190
|
& tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
|
|
172
191
|
& .crisp-datatable-entity-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
|
|
173
192
|
& .crisp-datatable-entity-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
174
193
|
& .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
|
|
175
|
-
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
|
|
194
|
+
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
|
|
195
|
+
& .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
|
|
196
|
+
& .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
|
|
197
|
+
& .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
|
|
198
|
+
& .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
|
|
199
|
+
& .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
176
200
|
& .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
|
|
177
201
|
& .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
|
|
178
202
|
& .crisp-datatable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
|
|
@@ -185,24 +209,70 @@
|
|
|
185
209
|
& .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
|
|
186
210
|
& .crisp-datatable-num { font-variant-numeric: tabular-nums; }
|
|
187
211
|
& .crisp-datatable-muted { color: var(--crisp-fg-muted); }
|
|
188
|
-
/*
|
|
212
|
+
/* crisp#822 -- a date/datetime cell's danger/overdue tone: same token as
|
|
213
|
+
TasksTable's own overdue due-date (crisp#462), plain text colour, no fill/weight change. */
|
|
214
|
+
& .crisp-datatable-danger { color: var(--crisp-fg-danger); }
|
|
215
|
+
/* url/email cell types (#654) -- same measured values as RecordsTable's
|
|
216
|
+
own .crisp-recordstable-link, which this mirrors: no underline at
|
|
217
|
+
rest, underline on hover, own-colour text rather than a browser-blue
|
|
218
|
+
link. */
|
|
189
219
|
& .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
|
|
190
220
|
& .crisp-datatable-link:hover { text-decoration: underline; }
|
|
191
|
-
/*
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
221
|
+
/* phone cell type/editor (#765) -- measured live: the dial-code prefix
|
|
222
|
+
is a muted rgba(0,0,0,0.63) at 14px, matching every other muted-text
|
|
223
|
+
value already measured elsewhere in this file. */
|
|
224
|
+
& .crisp-datatable-phone { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
|
|
225
|
+
& .crisp-datatable-phone-dial { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
|
|
226
|
+
& .crisp-datatable-phone-editor { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
|
|
227
|
+
& .crisp-datatable-phone-editor input { flex: 1; min-width: 0; }
|
|
228
|
+
/* multi-value phone (#816) -- confirmed live (a real custom "Multiple
|
|
229
|
+
values" Phone Number attribute, and the built-in People "Phone
|
|
230
|
+
numbers" field): the collapsed cell is a 4px-gap flex row of real
|
|
231
|
+
tel: links in a measured link blue (~rgb(59,108,232) pixel-sampled;
|
|
232
|
+
reused as the closest already-tokened blue, brand600, rather than
|
|
233
|
+
adding a third blue token for a delta this small), underlined,
|
|
234
|
+
UNLIKE the single-value cell above (plain text, no link, no
|
|
235
|
+
underline) -- multi-value is the one shape Attio makes into a real
|
|
236
|
+
link. The cell hard-clips on overflow (no ellipsis -- confirmed
|
|
237
|
+
live, a second value can end mid-digit with no "..."). */
|
|
238
|
+
& .crisp-datatable-phone-multi { display: inline-flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
|
|
239
|
+
& .crisp-datatable-phone-link { color: var(--crisp-bg-brand-solid); text-decoration: underline; white-space: nowrap; }
|
|
240
|
+
/* multi-value editor (#816) -- confirmed live: a stack of committed-
|
|
241
|
+
value rows (32px = size.menuRow, 5px 8px padding, 8px gap between
|
|
242
|
+
the grip/value/actions, 16px icons) above one ALWAYS-blank trailing
|
|
243
|
+
DataPhoneEditor-shaped row -- that blank row is the only "add
|
|
244
|
+
another" affordance, there's no separate button for it. The copy/delete pair reuses .crisp-datatable-entity-action's own
|
|
245
|
+
hover-reveal chrome (not independently re-measured as its own hit
|
|
246
|
+
target this pass, only the 16px icon glyphs were). */
|
|
247
|
+
& .crisp-datatable-phone-multi-editor { display: flex; flex-direction: column; }
|
|
248
|
+
& .crisp-datatable-phone-multi-row { display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); padding: 5px var(--crisp-space-2); }
|
|
249
|
+
& .crisp-datatable-phone-multi-row .crisp-datatable-phone-link { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
250
|
+
& .crisp-datatable-phone-multi-grip { color: var(--crisp-fg-muted); flex-shrink: 0; cursor: grab; }
|
|
251
|
+
& .crisp-datatable-phone-multi-actions { display: flex; align-items: center; gap: var(--crisp-space-1); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
|
|
252
|
+
& .crisp-datatable-phone-multi-row:hover .crisp-datatable-phone-multi-actions,
|
|
253
|
+
& .crisp-datatable-phone-multi-actions:focus-within { opacity: 1; }
|
|
254
|
+
& .crisp-datatable-phone-multi-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
|
|
255
|
+
& .crisp-datatable-phone-multi-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
256
|
+
/* datetime cell editor (#762) -- DatePickerField + TimePicker side by side. */
|
|
257
|
+
& .crisp-datatable-datetime-editor { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
|
|
258
|
+
/* rating cell type/editor (#760) -- measured live: 16px stars on an
|
|
259
|
+
18px pitch (a 2px gap), an empty star at rgba(0,0,0,0.1), a
|
|
260
|
+
committed/filled star at brand600 (--crisp-bg-brand-solid, an exact
|
|
261
|
+
match to the measured #266df0), and a distinct hover-preview colour
|
|
262
|
+
-- rgb(33,91,196) -- that's close to but NOT brand700/bgBrandSolidPressed (#1f5fd6); kept as its own literal rather than forced onto that token, since the two were measured stable and different. The read-only svg and the editable button's own inner svg both carry the
|
|
263
|
+
crisp-datatable-rating-star class on the svg itself (not the
|
|
264
|
+
button), so one set of fill rules covers both -- the button only
|
|
265
|
+
adds its own reset + cursor via a separate -btn class. */
|
|
195
266
|
& .crisp-datatable-rating { display: inline-flex; align-items: center; gap: 2px; }
|
|
196
267
|
& .crisp-datatable-rating-star { display: block; }
|
|
197
268
|
& .crisp-datatable-rating-star path { fill: rgba(0, 0, 0, 0.1); }
|
|
198
269
|
& .crisp-datatable-rating-star[data-filled] path { fill: var(--crisp-bg-brand-solid); }
|
|
199
270
|
& .crisp-datatable-rating-star[data-hovering] path { fill: #215bc4; }
|
|
200
271
|
& .crisp-datatable-rating-star-btn { display: inline-flex; padding: 0; margin: 0; border: none; background: none; font-family: inherit; cursor: pointer; }
|
|
201
|
-
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
202
|
-
hairline doubling as the drop indicator (it tints toward the brand
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
pointer-events implementation. */
|
|
272
|
+
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
273
|
+
a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
|
|
274
|
+
draggable + dataTransfer, the same one column reorder already uses
|
|
275
|
+
(RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
|
|
206
276
|
& .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
|
|
207
277
|
& .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
|
|
208
278
|
& .crisp-datatable-grip:active { cursor: grabbing; }
|
|
@@ -103,15 +103,20 @@ const spec = defineSpec({
|
|
|
103
103
|
gap: "3px",
|
|
104
104
|
borderRadius: c.radius.sm,
|
|
105
105
|
},
|
|
106
|
-
// crisp#
|
|
107
|
-
//
|
|
108
|
-
//
|
|
106
|
+
// crisp#805: measured live against Attio's real status/stage chip
|
|
107
|
+
// (Deals "Deal stage" column, All Deals table view) -- radius 9px, not
|
|
108
|
+
// `radius.chip`'s 7px, and asymmetric padding (room for a leading dot/
|
|
109
|
+
// icon: 1px top/bottom, 6px trailing, 4px leading), not a symmetric
|
|
110
|
+
// 9px. Height/font/tracking are unchanged (still not independently
|
|
111
|
+
// re-measured this round, per #696's own note).
|
|
109
112
|
large: {
|
|
110
113
|
height: c.size.chip,
|
|
111
|
-
|
|
114
|
+
paddingBlock: c.space.px,
|
|
115
|
+
paddingInlineStart: c.space["1"],
|
|
116
|
+
paddingInlineEnd: c.space["1.5"],
|
|
112
117
|
fontSize: c.text.lg,
|
|
113
118
|
letterSpacing: "-0.1px",
|
|
114
|
-
borderRadius: c.radius.
|
|
119
|
+
borderRadius: c.radius.lg,
|
|
115
120
|
},
|
|
116
121
|
},
|
|
117
122
|
variant: { soft: {}, solid: {} },
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const COUNTRIES = [
|
|
2
|
+
{ iso2: "US", name: "United States", dialCode: "1" },
|
|
3
|
+
{ iso2: "CA", name: "Canada", dialCode: "1" },
|
|
4
|
+
{ iso2: "GB", name: "United Kingdom", dialCode: "44" },
|
|
5
|
+
{ iso2: "IE", name: "Ireland", dialCode: "353" },
|
|
6
|
+
{ iso2: "FR", name: "France", dialCode: "33" },
|
|
7
|
+
{ iso2: "DE", name: "Germany", dialCode: "49" },
|
|
8
|
+
{ iso2: "ES", name: "Spain", dialCode: "34" },
|
|
9
|
+
{ iso2: "PT", name: "Portugal", dialCode: "351" },
|
|
10
|
+
{ iso2: "IT", name: "Italy", dialCode: "39" },
|
|
11
|
+
{ iso2: "NL", name: "Netherlands", dialCode: "31" },
|
|
12
|
+
{ iso2: "BE", name: "Belgium", dialCode: "32" },
|
|
13
|
+
{ iso2: "CH", name: "Switzerland", dialCode: "41" },
|
|
14
|
+
{ iso2: "AT", name: "Austria", dialCode: "43" },
|
|
15
|
+
{ iso2: "SE", name: "Sweden", dialCode: "46" },
|
|
16
|
+
{ iso2: "NO", name: "Norway", dialCode: "47" },
|
|
17
|
+
{ iso2: "DK", name: "Denmark", dialCode: "45" },
|
|
18
|
+
{ iso2: "FI", name: "Finland", dialCode: "358" },
|
|
19
|
+
{ iso2: "PL", name: "Poland", dialCode: "48" },
|
|
20
|
+
{ iso2: "CZ", name: "Czechia", dialCode: "420" },
|
|
21
|
+
{ iso2: "GR", name: "Greece", dialCode: "30" },
|
|
22
|
+
{ iso2: "RO", name: "Romania", dialCode: "40" },
|
|
23
|
+
{ iso2: "HU", name: "Hungary", dialCode: "36" },
|
|
24
|
+
{ iso2: "UA", name: "Ukraine", dialCode: "380" },
|
|
25
|
+
{ iso2: "RU", name: "Russia", dialCode: "7" },
|
|
26
|
+
{ iso2: "TR", name: "Turkey", dialCode: "90" },
|
|
27
|
+
{ iso2: "IL", name: "Israel", dialCode: "972" },
|
|
28
|
+
{ iso2: "AE", name: "United Arab Emirates", dialCode: "971" },
|
|
29
|
+
{ iso2: "SA", name: "Saudi Arabia", dialCode: "966" },
|
|
30
|
+
{ iso2: "EG", name: "Egypt", dialCode: "20" },
|
|
31
|
+
{ iso2: "ZA", name: "South Africa", dialCode: "27" },
|
|
32
|
+
{ iso2: "NG", name: "Nigeria", dialCode: "234" },
|
|
33
|
+
{ iso2: "KE", name: "Kenya", dialCode: "254" },
|
|
34
|
+
{ iso2: "IN", name: "India", dialCode: "91" },
|
|
35
|
+
{ iso2: "PK", name: "Pakistan", dialCode: "92" },
|
|
36
|
+
{ iso2: "BD", name: "Bangladesh", dialCode: "880" },
|
|
37
|
+
{ iso2: "LK", name: "Sri Lanka", dialCode: "94" },
|
|
38
|
+
{ iso2: "NP", name: "Nepal", dialCode: "977" },
|
|
39
|
+
{ iso2: "CN", name: "China", dialCode: "86" },
|
|
40
|
+
{ iso2: "HK", name: "Hong Kong", dialCode: "852" },
|
|
41
|
+
{ iso2: "TW", name: "Taiwan", dialCode: "886" },
|
|
42
|
+
{ iso2: "JP", name: "Japan", dialCode: "81" },
|
|
43
|
+
{ iso2: "KR", name: "South Korea", dialCode: "82" },
|
|
44
|
+
{ iso2: "MN", name: "Mongolia", dialCode: "976" },
|
|
45
|
+
{ iso2: "VN", name: "Vietnam", dialCode: "84" },
|
|
46
|
+
{ iso2: "TH", name: "Thailand", dialCode: "66" },
|
|
47
|
+
{ iso2: "SG", name: "Singapore", dialCode: "65" },
|
|
48
|
+
{ iso2: "MY", name: "Malaysia", dialCode: "60" },
|
|
49
|
+
{ iso2: "ID", name: "Indonesia", dialCode: "62" },
|
|
50
|
+
{ iso2: "PH", name: "Philippines", dialCode: "63" },
|
|
51
|
+
{ iso2: "AU", name: "Australia", dialCode: "61" },
|
|
52
|
+
{ iso2: "NZ", name: "New Zealand", dialCode: "64" },
|
|
53
|
+
{ iso2: "MX", name: "Mexico", dialCode: "52" },
|
|
54
|
+
{ iso2: "BR", name: "Brazil", dialCode: "55" },
|
|
55
|
+
{ iso2: "AR", name: "Argentina", dialCode: "54" },
|
|
56
|
+
{ iso2: "CL", name: "Chile", dialCode: "56" },
|
|
57
|
+
{ iso2: "CO", name: "Colombia", dialCode: "57" },
|
|
58
|
+
{ iso2: "PE", name: "Peru", dialCode: "51" },
|
|
59
|
+
];
|
|
60
|
+
/** Two Unicode Regional Indicator Symbols spell the flag emoji for `iso2`. */
|
|
61
|
+
export function flagEmoji(iso2) {
|
|
62
|
+
return [...iso2.toUpperCase()]
|
|
63
|
+
.map((ch) => String.fromCodePoint(0x1f1e6 - 65 + ch.charCodeAt(0)))
|
|
64
|
+
.join("");
|
|
65
|
+
}
|
|
66
|
+
export function countryByIso2(iso2) {
|
|
67
|
+
return COUNTRIES.find((c) => c.iso2 === iso2);
|
|
68
|
+
}
|