neba 1.16.0 → 1.16.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/components/color-picker/ColorPicker.d.ts +10 -2
- package/dist/components/color-picker/ColorPicker.js +1 -1
- package/dist/components/combobox/Combobox.d.ts +9 -2
- package/dist/components/combobox/Combobox.js +1 -1
- package/dist/components/date-picker/DatePicker.js +1 -1
- package/dist/components/date-range-picker/DateRangePicker.js +1 -1
- package/dist/components/date-time-picker/DateTimePicker.js +1 -1
- package/dist/components/menu/Menu.d.ts +6 -1
- package/dist/components/menu/Menu.js +1 -1
- package/dist/components/number-field/NumberField.d.ts +9 -4
- package/dist/components/number-field/NumberField.js +1 -1
- package/dist/components/provider/NebaProvider.d.ts +2 -2
- package/dist/components/provider/NebaProvider.js +1 -1
- package/dist/components/select/Select.d.ts +9 -2
- package/dist/components/select/Select.js +1 -1
- package/dist/components/text-field/TextField.d.ts +13 -5
- package/dist/components/text-field/TextField.js +1 -1
- package/dist/components/time-picker/TimePicker.js +1 -1
- package/dist/components/tree-select/TreeSelect.js +1 -1
- package/dist/internal/defaults.d.ts +8 -4
- package/dist/internal/notch.d.ts +80 -0
- package/dist/internal/notch.js +1 -0
- package/dist/internal/picker.d.ts +11 -3
- package/dist/internal/picker.js +1 -1
- package/dist/internal/styles.d.ts +24 -0
- package/dist/internal/styles.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind.css +257 -0
- package/dist/types.d.ts +18 -0
- package/package.json +15 -15
package/dist/tailwind.css
CHANGED
|
@@ -1177,6 +1177,256 @@
|
|
|
1177
1177
|
}
|
|
1178
1178
|
}
|
|
1179
1179
|
|
|
1180
|
+
/* ---------------------------------------------------------------------------
|
|
1181
|
+
* The notch
|
|
1182
|
+
*
|
|
1183
|
+
* A field whose label stands on its top edge: `labelPlacement` `notch` or
|
|
1184
|
+
* `float`. The shell drops its own edge, and `internal/notch.tsx` draws one in
|
|
1185
|
+
* three pieces on a grid: the start, as wide as the corner's radius; the gap,
|
|
1186
|
+
* as wide as the label because the label sits in its column; and the end, which
|
|
1187
|
+
* takes the rest. The gap has no top line, and that missing line is the notch.
|
|
1188
|
+
*
|
|
1189
|
+
* Every piece reads its colour out of `--n-notch-line` and its top light out of
|
|
1190
|
+
* `--n-notch-top`, which the rules below set from the variant and from the state
|
|
1191
|
+
* of the element the notch sits in: the shell, or for a Select the frame that
|
|
1192
|
+
* holds the notch beside its trigger. The ring
|
|
1193
|
+
* is each piece's `::before`, two pixels outside it, and like every ring in the
|
|
1194
|
+
* library it is declared at no width and arrives in colour.
|
|
1195
|
+
*
|
|
1196
|
+
* It is CSS rather than Tailwind because the conditions are relations between
|
|
1197
|
+
* three elements — the holder's hover and focus, the control's emptiness and
|
|
1198
|
+
* the notch itself — and the Tailwind spelling of "a later sibling of an empty
|
|
1199
|
+
* input whose parent has no focus" is not something anybody should have to read.
|
|
1200
|
+
* ------------------------------------------------------------------------- */
|
|
1201
|
+
|
|
1202
|
+
/* The shell, and the label's width under it at no height, in one cell. */
|
|
1203
|
+
.neba-notch-frame {
|
|
1204
|
+
position: relative;
|
|
1205
|
+
display: grid;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.neba-notch-frame > * {
|
|
1209
|
+
grid-area: 1 / 1;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.neba-notch-sizer {
|
|
1213
|
+
height: 0;
|
|
1214
|
+
overflow: hidden;
|
|
1215
|
+
visibility: hidden;
|
|
1216
|
+
white-space: nowrap;
|
|
1217
|
+
padding-inline: calc(var(--n-notch-r) + var(--n-notch-gap));
|
|
1218
|
+
font-size: var(--n-float-size);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
.neba-notch-sizer[data-sample]::before {
|
|
1222
|
+
content: attr(data-sample);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.neba-notch {
|
|
1226
|
+
position: absolute;
|
|
1227
|
+
inset: var(--n-notch-inset, 0);
|
|
1228
|
+
display: grid;
|
|
1229
|
+
grid-template-columns:
|
|
1230
|
+
var(--n-notch-r)
|
|
1231
|
+
fit-content(calc(100% - 2 * var(--n-notch-r)))
|
|
1232
|
+
minmax(var(--n-notch-r), 1fr);
|
|
1233
|
+
grid-template-rows: 100%;
|
|
1234
|
+
border-radius: inherit;
|
|
1235
|
+
pointer-events: none;
|
|
1236
|
+
|
|
1237
|
+
/* The label on the edge, and the gap open under it. A resting label swaps
|
|
1238
|
+
every one of these below. */
|
|
1239
|
+
--n-label-top: calc(0.5px - var(--n-float-leading) / 2);
|
|
1240
|
+
--n-label-x: 0px;
|
|
1241
|
+
--n-label-size: var(--n-float-size);
|
|
1242
|
+
--n-label-leading: var(--n-float-leading);
|
|
1243
|
+
--n-gap-line: transparent;
|
|
1244
|
+
--n-gap-top: transparent;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
.neba-notch[data-variant='outline'] {
|
|
1248
|
+
--n-notch-line: var(--n-line);
|
|
1249
|
+
--n-notch-top: var(--neba-plate-top);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.neba-notch[data-variant='solid'] {
|
|
1253
|
+
--n-notch-line: var(--neba-plate-edge);
|
|
1254
|
+
--n-notch-top: var(--neba-plate-top);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
@media (hover: hover) {
|
|
1258
|
+
:hover > .neba-notch[data-variant='outline']:not([data-state]) {
|
|
1259
|
+
--n-notch-line: var(--n-line-hover);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/* After the hover rule and at its specificity, so a pointer resting on a
|
|
1264
|
+
focused field leaves the edge the colour of the ring it is flush against. */
|
|
1265
|
+
:focus-within > .neba-notch[data-variant='outline']:not([data-state]) {
|
|
1266
|
+
--n-notch-line: var(--n-ring);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.neba-notch[data-state='disabled'] {
|
|
1270
|
+
--n-notch-top: transparent;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
.neba-notch[data-state='disabled'][data-variant='outline'] {
|
|
1274
|
+
--n-notch-line: var(--neba-disabled-border);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.neba-notch[data-state='disabled'][data-variant='solid'] {
|
|
1278
|
+
--n-notch-line: transparent;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
.neba-notch-start,
|
|
1282
|
+
.neba-notch-gap,
|
|
1283
|
+
.neba-notch-end {
|
|
1284
|
+
position: relative;
|
|
1285
|
+
grid-row: 1;
|
|
1286
|
+
border: 0 solid var(--n-notch-line, transparent);
|
|
1287
|
+
border-block-width: 1px;
|
|
1288
|
+
box-shadow: inset 0 1px 0 var(--n-notch-top, transparent);
|
|
1289
|
+
transition:
|
|
1290
|
+
border-color var(--neba-duration) var(--neba-ease),
|
|
1291
|
+
box-shadow var(--neba-duration) var(--neba-ease);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
.neba-notch-start {
|
|
1295
|
+
grid-column: 1;
|
|
1296
|
+
border-inline-start-width: 1px;
|
|
1297
|
+
border-start-start-radius: inherit;
|
|
1298
|
+
border-end-start-radius: inherit;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
.neba-notch-gap {
|
|
1302
|
+
grid-column: 2;
|
|
1303
|
+
border-top-color: var(--n-gap-line);
|
|
1304
|
+
box-shadow: inset 0 1px 0 var(--n-gap-top);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.neba-notch-end {
|
|
1308
|
+
grid-column: 3;
|
|
1309
|
+
border-inline-end-width: 1px;
|
|
1310
|
+
border-start-end-radius: inherit;
|
|
1311
|
+
border-end-end-radius: inherit;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/* The ring, drawn over the two pixels outside each piece. The radius is the
|
|
1315
|
+
corner's plus the ring's own width, so its inner edge meets the hairline. */
|
|
1316
|
+
.neba-notch-start::before,
|
|
1317
|
+
.neba-notch-gap::before,
|
|
1318
|
+
.neba-notch-end::before {
|
|
1319
|
+
content: '';
|
|
1320
|
+
position: absolute;
|
|
1321
|
+
inset-block: -3px;
|
|
1322
|
+
border: 0 solid transparent;
|
|
1323
|
+
transition: border-color var(--neba-duration) var(--neba-ease);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
.neba-notch-start::before {
|
|
1327
|
+
inset-inline: -3px 0;
|
|
1328
|
+
border-start-start-radius: calc(var(--n-notch-r) + 2px);
|
|
1329
|
+
border-end-start-radius: calc(var(--n-notch-r) + 2px);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
.neba-notch-gap::before {
|
|
1333
|
+
inset-inline: 0;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
.neba-notch-end::before {
|
|
1337
|
+
inset-inline: 0 -3px;
|
|
1338
|
+
border-start-end-radius: calc(var(--n-notch-r) + 2px);
|
|
1339
|
+
border-end-end-radius: calc(var(--n-notch-r) + 2px);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/* Only the bottom of the gap: a field with the focus in it has its label on
|
|
1343
|
+
the edge, so the top of the gap is always open when the ring is drawn. */
|
|
1344
|
+
:has(:focus-visible) > .neba-notch > .neba-notch-start::before {
|
|
1345
|
+
border-color: var(--n-ring);
|
|
1346
|
+
border-block-width: 2px;
|
|
1347
|
+
border-inline-start-width: 2px;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
:has(:focus-visible) > .neba-notch > .neba-notch-gap::before {
|
|
1351
|
+
border-color: var(--n-ring);
|
|
1352
|
+
border-block-end-width: 2px;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
:has(:focus-visible) > .neba-notch > .neba-notch-end::before {
|
|
1356
|
+
border-color: var(--n-ring);
|
|
1357
|
+
border-block-width: 2px;
|
|
1358
|
+
border-inline-end-width: 2px;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/*
|
|
1362
|
+
* The label, centred on the edge line. It moves on `top` and
|
|
1363
|
+
* `inset-inline-start` and changes size on `font-size`, never on a transform:
|
|
1364
|
+
* it is set again at every size on the way rather than scaled, so no frame of
|
|
1365
|
+
* it is a resampled picture of the word.
|
|
1366
|
+
*
|
|
1367
|
+
* A press goes straight through it to what is underneath. A resting label
|
|
1368
|
+
* covers most of the control, and a Select opens on the pointer going down on
|
|
1369
|
+
* its trigger, which a `<label>` in front of it would swallow; the input
|
|
1370
|
+
* under a field's label is where the caret belongs anyway.
|
|
1371
|
+
*/
|
|
1372
|
+
.neba-notch-label {
|
|
1373
|
+
grid-area: 1 / 2;
|
|
1374
|
+
align-self: start;
|
|
1375
|
+
position: relative;
|
|
1376
|
+
top: var(--n-label-top);
|
|
1377
|
+
inset-inline-start: var(--n-label-x);
|
|
1378
|
+
min-width: 0;
|
|
1379
|
+
padding-inline: var(--n-notch-gap);
|
|
1380
|
+
overflow: hidden;
|
|
1381
|
+
white-space: nowrap;
|
|
1382
|
+
text-overflow: ellipsis;
|
|
1383
|
+
font-size: var(--n-label-size);
|
|
1384
|
+
line-height: var(--n-label-leading);
|
|
1385
|
+
transition-property: top, inset-inline-start, font-size, line-height, color;
|
|
1386
|
+
transition-duration: var(--neba-duration);
|
|
1387
|
+
transition-timing-function: var(--neba-ease);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/*
|
|
1391
|
+
* A `float` label at rest: set in the control's own text, where the text would
|
|
1392
|
+
* start, with the gap closed over it. Three ways to know the field is empty,
|
|
1393
|
+
* one per kind of control — an input's `:placeholder-shown`, a trigger's
|
|
1394
|
+
* `data-placeholder`, and `data-empty` from a component that has no control to
|
|
1395
|
+
* ask — and the same rule for all three: nothing inside the holder has the
|
|
1396
|
+
* focus and nothing is open. Autofill counts as a value before the reader has
|
|
1397
|
+
* accepted it, because the text is already drawn where the label would be.
|
|
1398
|
+
*/
|
|
1399
|
+
:not(:focus-within) > .neba-float-control:placeholder-shown:not(:autofill) ~ .neba-notch-float,
|
|
1400
|
+
:not(:focus-within)
|
|
1401
|
+
> .neba-float-control[data-placeholder]:not([data-popup-open])
|
|
1402
|
+
~ .neba-notch-float,
|
|
1403
|
+
:not(:focus-within) > .neba-notch-float[data-empty] {
|
|
1404
|
+
--n-label-top: var(--n-rest-top);
|
|
1405
|
+
--n-label-x: var(--n-rest-x);
|
|
1406
|
+
--n-label-size: var(--n-rest-size);
|
|
1407
|
+
--n-label-leading: var(--n-rest-leading);
|
|
1408
|
+
--n-label-ink: var(--neba-muted-fg);
|
|
1409
|
+
--n-gap-line: var(--n-notch-line, transparent);
|
|
1410
|
+
--n-gap-top: var(--n-notch-top, transparent);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/* And the placeholder it is standing in for, which comes back with the focus.
|
|
1414
|
+
Everything inside a trigger's placeholder as well as the placeholder itself:
|
|
1415
|
+
a DateRangePicker's is two words and an arrow, each with an ink of its own.
|
|
1416
|
+
Spelled out rather than as `:is([data-placeholder] *)`, which would also match
|
|
1417
|
+
the chevron — a Select's trigger carries `data-placeholder` itself. */
|
|
1418
|
+
.neba-float-control::placeholder,
|
|
1419
|
+
.neba-float-control [data-placeholder],
|
|
1420
|
+
.neba-float-control [data-placeholder] * {
|
|
1421
|
+
transition: color var(--neba-duration) var(--neba-ease);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
.neba-float-control:not(:focus)::placeholder,
|
|
1425
|
+
.neba-float-control:not(:focus):not([data-popup-open]) [data-placeholder],
|
|
1426
|
+
.neba-float-control:not(:focus):not([data-popup-open]) [data-placeholder] * {
|
|
1427
|
+
color: transparent;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1180
1430
|
/* ---------------------------------------------------------------------------
|
|
1181
1431
|
* Forced colours
|
|
1182
1432
|
*
|
|
@@ -1200,6 +1450,13 @@
|
|
|
1200
1450
|
outline-offset: -1px;
|
|
1201
1451
|
}
|
|
1202
1452
|
|
|
1453
|
+
/* A forced palette paints a transparent line too, so a notched shell's own
|
|
1454
|
+
border and the closed top of the gap are both drawn through the label.
|
|
1455
|
+
The page under a forced palette is opaque, and so is this. */
|
|
1456
|
+
.neba-notch-label.neba-notch-label {
|
|
1457
|
+
background-color: Canvas;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1203
1460
|
/* Behind `@supports` as well, which every browser that can be in a forced
|
|
1204
1461
|
palette passes: a colour that cannot opt out of the palette would be
|
|
1205
1462
|
replaced by it, and a fill the same colour as its track says nothing. */
|
package/dist/types.d.ts
CHANGED
|
@@ -179,6 +179,24 @@ export type NebaVariant = 'solid' | 'outline' | 'text';
|
|
|
179
179
|
* pressing removes one, so a flat control still answers a press without moving.
|
|
180
180
|
*/
|
|
181
181
|
export type NebaElevation = 0 | 1 | 2 | 3;
|
|
182
|
+
/**
|
|
183
|
+
* Where a field-shaped component draws its label.
|
|
184
|
+
*
|
|
185
|
+
* - `top` — above the field, in a line of its own. The default.
|
|
186
|
+
* - `notch` — on the field's top edge, in a gap cut out of the border.
|
|
187
|
+
* - `float` — inside the field, where the placeholder would be, while the field
|
|
188
|
+
* is empty and nothing in it has the focus; it rises into the notch the
|
|
189
|
+
* moment either stops being true.
|
|
190
|
+
*
|
|
191
|
+
* `notch` and `float` take no height of their own. The label stands half a
|
|
192
|
+
* line above the field's top edge instead of a line above it, so a column of
|
|
193
|
+
* notched fields is shorter than the same column with their labels on top.
|
|
194
|
+
*
|
|
195
|
+
* A field that draws something at its start — a `startIcon`, the split
|
|
196
|
+
* steppers of a NumberField — keeps a `float` label in the notch, because the
|
|
197
|
+
* place it would rest is taken.
|
|
198
|
+
*/
|
|
199
|
+
export type NebaLabelPlacement = 'top' | 'notch' | 'float';
|
|
182
200
|
/**
|
|
183
201
|
* The seven effects the `transition` prop takes, which is the **entrance**
|
|
184
202
|
* vocabulary: everything a component can be given to arrive on.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neba",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.2",
|
|
4
4
|
"description": "A comprehensive React UI component library — buttons, fields, menus, dialogs, tables and much more — accessible and themeable, sharing one prop vocabulary and a translucent acrylic design language. ESM only, types included, dark mode built in.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -154,15 +154,15 @@
|
|
|
154
154
|
"@a2ui/react": "^0.11.1",
|
|
155
155
|
"@a2ui/web_core": "^0.11.0",
|
|
156
156
|
"@eslint/js": "^9.39.1",
|
|
157
|
-
"@mdn/browser-compat-data": "^8.1.
|
|
157
|
+
"@mdn/browser-compat-data": "^8.1.3",
|
|
158
158
|
"@tailwindcss/postcss": "^4.3.3",
|
|
159
|
-
"@types/node": "^26.
|
|
160
|
-
"@types/react": "^19.
|
|
161
|
-
"@types/react-dom": "^19.
|
|
162
|
-
"@typescript-eslint/parser": "^8.
|
|
159
|
+
"@types/node": "^26.6.3",
|
|
160
|
+
"@types/react": "^19.3.0",
|
|
161
|
+
"@types/react-dom": "^19.3.0",
|
|
162
|
+
"@typescript-eslint/parser": "^8.70.1",
|
|
163
163
|
"@vitejs/plugin-react": "^6.1.1",
|
|
164
|
-
"@vitest/browser": "^5.0.
|
|
165
|
-
"@vitest/browser-playwright": "^5.0.
|
|
164
|
+
"@vitest/browser": "^5.0.2",
|
|
165
|
+
"@vitest/browser-playwright": "^5.0.2",
|
|
166
166
|
"eslint": "^9.39.1",
|
|
167
167
|
"eslint-config-prettier": "^10.1.8",
|
|
168
168
|
"eslint-plugin-n": "^18.3.0",
|
|
@@ -171,20 +171,20 @@
|
|
|
171
171
|
"jiti": "^2.7.0",
|
|
172
172
|
"playwright": "^1.63.0",
|
|
173
173
|
"postcss": "^8.5.28",
|
|
174
|
-
"prettier": "^3.9.
|
|
174
|
+
"prettier": "^3.9.9",
|
|
175
175
|
"prettier-plugin-tailwindcss": "^0.8.1",
|
|
176
|
-
"react": "^19.
|
|
177
|
-
"react-dom": "^19.
|
|
176
|
+
"react": "^19.3.0",
|
|
177
|
+
"react-dom": "^19.3.0",
|
|
178
178
|
"tailwindcss": "^4.3.3",
|
|
179
179
|
"terser": "^5.51.2",
|
|
180
|
-
"tsx": "^4.23.
|
|
180
|
+
"tsx": "^4.23.15",
|
|
181
181
|
"typescript": "^6.0.3",
|
|
182
|
-
"typescript-eslint": "^8.
|
|
183
|
-
"vite": "^8.
|
|
182
|
+
"typescript-eslint": "^8.70.1",
|
|
183
|
+
"vite": "^8.3.1",
|
|
184
184
|
"vitepress": "^1.6.4",
|
|
185
185
|
"vitepress-i18n": "^1.4.0",
|
|
186
186
|
"vitepress-sidebar": "^1.40.0",
|
|
187
|
-
"vitest": "^5.0.
|
|
187
|
+
"vitest": "^5.0.2",
|
|
188
188
|
"vitest-browser-react": "^2.3.0",
|
|
189
189
|
"zod": "^3.25.76"
|
|
190
190
|
},
|