@jinaan/uikit 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinaan/uikit",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Reusable React and Tailwind UI components for Jinaan projects.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -145,9 +145,6 @@ interface DataTableProps<T> {
145
145
  const HEADER_CELL =
146
146
  "h-[var(--table-header-h)] whitespace-nowrap px-[var(--table-pad-x)] text-left align-middle text-[0.8125rem] font-medium";
147
147
 
148
- const TOOLBAR_BUTTON =
149
- "rounded-[6px] bg-surface text-fg shadow-xs ring-1 ring-line hover:bg-elevated hover:text-fg active:bg-elevated";
150
-
151
148
  function initials(name: string): string {
152
149
  return name
153
150
  .split(/\s+/)
@@ -356,7 +353,6 @@ export function DataTable<T>({
356
353
  <div className="flex flex-wrap items-center gap-2">
357
354
  {search && (
358
355
  <Input
359
- size="sm"
360
356
  type="search"
361
357
  aria-label={search.placeholder ?? "Search"}
362
358
  placeholder={search.placeholder ?? "Search..."}
@@ -373,10 +369,9 @@ export function DataTable<T>({
373
369
  <DropdownMenuTrigger asChild>
374
370
  <Button
375
371
  variant="secondary"
376
- size="sm"
372
+ size="md"
377
373
  leadingIcon={<ColumnsIcon />}
378
374
  trailingIcon={<CaretDown />}
379
- className={TOOLBAR_BUTTON}
380
375
  >
381
376
  Columns
382
377
  </Button>
@@ -419,10 +414,9 @@ export function DataTable<T>({
419
414
  <DropdownMenuTrigger asChild>
420
415
  <Button
421
416
  variant="secondary"
422
- size="sm"
417
+ size="md"
423
418
  iconOnly
424
419
  aria-label="Add filter"
425
- className={TOOLBAR_BUTTON}
426
420
  >
427
421
  <FunnelSimple />
428
422
  </Button>
@@ -443,70 +437,74 @@ export function DataTable<T>({
443
437
  </DropdownMenuContent>
444
438
  </DropdownMenu>
445
439
  ) : null}
446
- {toolbarEnd ? (
447
- <div className="flex-0 sm:ml-auto [&_button]:h-8 [&_button]:rounded-[6px]">
448
- {toolbarEnd}
449
- </div>
450
- ) : null}
440
+ {toolbarEnd ? <div className="flex-0 sm:ml-auto">{toolbarEnd}</div> : null}
451
441
  </div>
452
442
  )}
453
443
 
454
444
  {activeFilters.length > 0 ? (
455
445
  <div className="flex flex-wrap items-center gap-2">
456
- {activeFilters.map((filter) => (
457
- <DropdownMenu key={filter.id}>
458
- <DropdownMenuTrigger asChild>
446
+ {activeFilters.map((filter) => {
447
+ const clear = () => {
448
+ filter.onChange([]);
449
+ setActiveFilterIds((prev) => prev.filter((id) => id !== filter.id));
450
+ };
451
+
452
+ return (
453
+ <div key={filter.id} className="inline-flex items-center">
459
454
  <Button
460
- variant="outline"
461
- size="sm"
462
- className="rounded-[6px] bg-surface shadow-xs"
455
+ variant="secondary"
456
+ size="md"
457
+ iconOnly
458
+ aria-label={`Remove ${filter.label} filter`}
459
+ className="rounded-r-none"
460
+ onClick={clear}
463
461
  >
464
- <FunnelSimple />
465
- <span className="font-medium">{filter.label}</span>
466
- <span className="h-4 w-px bg-line" aria-hidden />
467
- <span className="text-muted">
468
- {filter.value.length > 0
469
- ? `${filter.value.length} selected`
470
- : "Enter value"}
471
- </span>
472
- <CaretDown className="text-muted" />
462
+ <X />
473
463
  </Button>
474
- </DropdownMenuTrigger>
475
- <DropdownMenuContent align="start" className="w-56">
476
- <DropdownMenuLabel className="text-[13px]">
477
- Filter by {filter.label}
478
- </DropdownMenuLabel>
479
- {filter.options.map((option) => {
480
- const checked = filter.value.includes(option.value);
481
- return (
482
- <DropdownMenuCheckboxItem
483
- key={option.value}
484
- checked={checked}
485
- onCheckedChange={() =>
486
- filter.onChange(
487
- checked
488
- ? filter.value.filter((value) => value !== option.value)
489
- : [...filter.value, option.value],
490
- )
491
- }
492
- onSelect={(e) => e.preventDefault()}
493
- >
494
- {option.label}
495
- </DropdownMenuCheckboxItem>
496
- );
497
- })}
498
- <DropdownMenuSeparator />
499
- <DropdownMenuItem
500
- onSelect={() => {
501
- filter.onChange([]);
502
- setActiveFilterIds((prev) => prev.filter((id) => id !== filter.id));
503
- }}
504
- >
505
- Clear filter
506
- </DropdownMenuItem>
507
- </DropdownMenuContent>
508
- </DropdownMenu>
509
- ))}
464
+ <DropdownMenu>
465
+ <DropdownMenuTrigger asChild>
466
+ <Button variant="secondary" size="md" className="rounded-l-none pl-2">
467
+ <FunnelSimple />
468
+ <span className="font-medium">{filter.label}</span>
469
+ <span className="h-4 w-px bg-line" aria-hidden />
470
+ <span className="text-muted">
471
+ {filter.value.length > 0
472
+ ? `${filter.value.length} selected`
473
+ : "Enter value"}
474
+ </span>
475
+ <CaretDown className="text-muted" />
476
+ </Button>
477
+ </DropdownMenuTrigger>
478
+ <DropdownMenuContent align="start" className="w-56">
479
+ <DropdownMenuLabel className="text-[13px]">
480
+ Filter by {filter.label}
481
+ </DropdownMenuLabel>
482
+ {filter.options.map((option) => {
483
+ const checked = filter.value.includes(option.value);
484
+ return (
485
+ <DropdownMenuCheckboxItem
486
+ key={option.value}
487
+ checked={checked}
488
+ onCheckedChange={() =>
489
+ filter.onChange(
490
+ checked
491
+ ? filter.value.filter((value) => value !== option.value)
492
+ : [...filter.value, option.value],
493
+ )
494
+ }
495
+ onSelect={(e) => e.preventDefault()}
496
+ >
497
+ {option.label}
498
+ </DropdownMenuCheckboxItem>
499
+ );
500
+ })}
501
+ <DropdownMenuSeparator />
502
+ <DropdownMenuItem onSelect={clear}>Clear filter</DropdownMenuItem>
503
+ </DropdownMenuContent>
504
+ </DropdownMenu>
505
+ </div>
506
+ );
507
+ })}
510
508
  <button
511
509
  type="button"
512
510
  onClick={() => {
@@ -529,7 +527,7 @@ export function DataTable<T>({
529
527
  {(bulkActions ?? []).map((a) => (
530
528
  <Button
531
529
  key={a.label}
532
- size="sm"
530
+ size="md"
533
531
  variant={a.variant ?? "outline"}
534
532
  disabled={a.disabled}
535
533
  onClick={() => void a.onRun(selectedRows)}
@@ -567,7 +565,7 @@ export function DataTable<T>({
567
565
  <thead>
568
566
  <tr>
569
567
  {selectable && (
570
- <th className="h-[var(--table-header-h)] w-11 px-4 align-middle">
568
+ <th className="h-(--table-header-h) w-11 px-4 align-middle">
571
569
  <Checkbox
572
570
  aria-label="Select all rows"
573
571
  checked={allSelected ? true : someSelected ? "indeterminate" : false}
@@ -611,7 +609,7 @@ export function DataTable<T>({
611
609
  </th>
612
610
  ))}
613
611
  {rowActions && (
614
- <th className="h-[var(--table-header-h)] w-44 px-4 align-middle">
612
+ <th className="h-(--table-header-h) w-44 px-4 align-middle">
615
613
  <span className="sr-only">Actions</span>
616
614
  </th>
617
615
  )}
@@ -627,8 +625,8 @@ export function DataTable<T>({
627
625
  ))}
628
626
  </div>
629
627
  ) : error ? (
630
- <div className="flex min-h-[22rem] flex-col items-center justify-center gap-4 px-6 py-16 text-center">
631
- <div className="flex size-[var(--table-empty-icon)] items-center justify-center rounded-2xl border border-table-empty-icon-border bg-table-empty-icon text-error [&_svg]:size-8">
628
+ <div className="flex min-h-88 flex-col items-center justify-center gap-4 px-6 py-16 text-center">
629
+ <div className="flex size-(--table-empty-icon) items-center justify-center rounded-2xl border border-table-empty-icon-border bg-table-empty-icon text-error [&_svg]:size-8">
632
630
  <WarningCircle weight="duotone" />
633
631
  </div>
634
632
  <div className="space-y-2">
@@ -718,7 +716,7 @@ export function DataTable<T>({
718
716
  </span>
719
717
  <div className="flex items-center gap-2">
720
718
  <Select value={String(pageSize)} onValueChange={(v) => setPageSize(Number(v))}>
721
- <SelectTrigger aria-label="Rows per page" className="h-8 w-[110px]">
719
+ <SelectTrigger aria-label="Rows per page" className="w-27.5">
722
720
  <SelectValue />
723
721
  </SelectTrigger>
724
722
  <SelectContent>
@@ -731,7 +729,7 @@ export function DataTable<T>({
731
729
  </Select>
732
730
  <Button
733
731
  variant="outline"
734
- size="sm"
732
+ size="md"
735
733
  disabled={page <= 0}
736
734
  onClick={() => setPage((p) => Math.max(0, p - 1))}
737
735
  >
@@ -739,7 +737,7 @@ export function DataTable<T>({
739
737
  </Button>
740
738
  <Button
741
739
  variant="outline"
742
- size="sm"
740
+ size="md"
743
741
  disabled={page >= pageCount - 1}
744
742
  onClick={() => setPage((p) => Math.min(pageCount - 1, p + 1))}
745
743
  >
@@ -22,7 +22,7 @@ export const DropdownMenuContent = forwardRef<
22
22
  ref={ref}
23
23
  sideOffset={sideOffset}
24
24
  className={cn(
25
- "z-50 min-w-48 overflow-hidden rounded-lg border border-line bg-surface p-1 shadow-md",
25
+ "z-50 min-w-48 overflow-hidden rounded-lg border border-line bg-[var(--button-secondary-bg)] p-1 shadow-md",
26
26
  "data-[state=open]:animate-in data-[state=closed]:animate-out",
27
27
  "data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
28
28
  "data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95",
@@ -45,7 +45,7 @@ export const DropdownMenuItem = forwardRef<
45
45
  ref={ref}
46
46
  className={cn(
47
47
  "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-fg outline-none transition-colors",
48
- "focus:bg-elevated data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
48
+ "focus:bg-[var(--menu-highlight)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
49
49
  "[&_svg]:size-4 [&_svg]:text-muted",
50
50
  inset && "pl-8",
51
51
  className,
@@ -62,15 +62,15 @@ export const DropdownMenuCheckboxItem = forwardRef<
62
62
  <DropdownMenuPrimitive.CheckboxItem
63
63
  ref={ref}
64
64
  className={cn(
65
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm text-fg outline-none transition-colors focus:bg-elevated",
65
+ "group relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm text-fg outline-none transition-colors focus:bg-[var(--menu-highlight)]",
66
66
  className,
67
67
  )}
68
68
  checked={checked}
69
69
  {...props}
70
70
  >
71
- <span className="absolute left-2 flex size-4 items-center justify-center">
72
- <DropdownMenuPrimitive.ItemIndicator>
73
- <Check className="size-3.5" />
71
+ <span className="absolute left-2 flex size-4 shrink-0 items-center justify-center rounded border border-line-strong bg-transparent transition-colors group-data-[state=checked]:border-primary">
72
+ <DropdownMenuPrimitive.ItemIndicator className="btn-fill btn-inner-border flex size-full items-center justify-center rounded-[3px] border-0 bg-[var(--button-primary-bg)] text-[var(--button-primary-fg)] [--button-inner:var(--button-primary-inner)]">
73
+ <Check className="size-3" weight="bold" />
74
74
  </DropdownMenuPrimitive.ItemIndicator>
75
75
  </span>
76
76
  {children}
package/src/input.tsx CHANGED
@@ -110,17 +110,17 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
110
110
  : undefined
111
111
  }
112
112
  className={cn(
113
- "flex w-full rounded-[var(--control-radius)] border bg-[var(--input-bg)] text-[var(--input-fg)] transition-colors",
113
+ "flex w-full rounded-(--control-radius) border bg-(--input-bg) text-(--input-fg) transition-colors",
114
114
  HEIGHT[size],
115
115
  PAD_X[size],
116
- "placeholder:text-[var(--input-placeholder)]",
117
- "hover:border-[var(--input-border-hover)]",
118
- "focus-visible:outline-none focus-visible:border-[var(--input-border-focus)] focus-visible:ring-2 focus-visible:ring-focus/30",
116
+ "placeholder:text-(--input-placeholder)",
117
+ "hover:border-(--input-border-hover)",
118
+ "focus-visible:outline-none focus-visible:border-(--input-border-focus) focus-visible:ring-2 focus-visible:ring-focus/30",
119
119
  error &&
120
120
  "border-error hover:border-error focus-visible:border-error focus-visible:ring-error/30",
121
- !error && "border-[var(--input-border)]",
121
+ !error && "border-(--input-border)",
122
122
  disabled &&
123
- "cursor-not-allowed bg-[var(--input-bg-disabled)] opacity-60 hover:border-[var(--input-border)]",
123
+ "cursor-not-allowed bg-(--input-bg-disabled) opacity-60 hover:border-(--input-border)",
124
124
  readOnly && "cursor-default focus-visible:ring-0",
125
125
  className,
126
126
  )}
@@ -141,7 +141,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
141
141
  {leadingIcon ? (
142
142
  <span
143
143
  className={cn(
144
- "pointer-events-none absolute inset-y-0 flex items-center text-[var(--input-placeholder)]",
144
+ "pointer-events-none absolute inset-y-0 flex items-center text-(--input-placeholder)",
145
145
  LEAD_INSET[size],
146
146
  )}
147
147
  aria-hidden
@@ -158,7 +158,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
158
158
  aria-label="Clear search"
159
159
  onClick={onClear}
160
160
  className={cn(
161
- "absolute inset-y-0 flex items-center text-[var(--input-placeholder)] transition-colors hover:text-[var(--input-fg)]",
161
+ "absolute inset-y-0 flex items-center text-(--input-placeholder) transition-colors hover:text-(--input-fg)",
162
162
  TRAIL_INSET[size],
163
163
  )}
164
164
  >
@@ -169,7 +169,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
169
169
  ) : trailingIcon ? (
170
170
  <span
171
171
  className={cn(
172
- "pointer-events-none absolute inset-y-0 flex items-center text-[var(--input-placeholder)]",
172
+ "pointer-events-none absolute inset-y-0 flex items-center text-(--input-placeholder)",
173
173
  TRAIL_INSET[size],
174
174
  )}
175
175
  aria-hidden
package/styles.css CHANGED
@@ -41,7 +41,7 @@
41
41
 
42
42
  /* Data tables: header band is the darkest layer, the body panel sits above it
43
43
  and the page is lighter than the header. */
44
- --table-container-bg: #f1f1f3;
44
+ --table-container-bg: #e9e9ec;
45
45
  --table-panel-bg: #ffffff;
46
46
  --table-panel-border: #e3e3e7;
47
47
  --table-header-text: #5f5f6f;
@@ -105,6 +105,7 @@
105
105
  --button-surface-hover: #f1f1f3;
106
106
  --button-surface-active: #e9e9ec;
107
107
  --button-ghost-hover: var(--button-surface-hover);
108
+ --menu-highlight: color-mix(in oklab, var(--button-secondary-bg) 85%, black);
108
109
  --button-destructive-bg: var(--error);
109
110
  --button-destructive-fg: #ffffff;
110
111
  /* Destructive keeps its red identity; hover/pressed are red-shifted. */