@jinaan/uikit 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ import { type ReactNode } from "react";
2
+ /** Count matching elements anywhere in a static React children tree. */
3
+ export declare function countDescendants(children: ReactNode, types: readonly unknown[]): number;
4
+ //# sourceMappingURL=item-count.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-count.d.ts","sourceRoot":"","sources":["../src/item-count.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjE,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,SAAS,EACnB,KAAK,EAAE,SAAS,OAAO,EAAE,GACxB,MAAM,CAcR"}
@@ -13,7 +13,7 @@ interface SegmentedProps<T extends string> {
13
13
  "aria-label"?: string;
14
14
  }
15
15
  /**
16
- * Single-choice control for a small set of options, styled after the Clerk
16
+ * Single-choice control for a small set of options, styled after the Jinaan
17
17
  * dashboard: a recessed track with an elevated selected pill. Implemented as a
18
18
  * `radiogroup` of native radios, so arrow-key roving and forms work natively.
19
19
  */
package/dist/select.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as SelectPrimitive from "@radix-ui/react-select";
2
2
  import { type ComponentPropsWithoutRef } from "react";
3
- export declare const Select: import("react").FC<SelectPrimitive.SelectProps>;
4
3
  export declare const SelectGroup: import("react").ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & import("react").RefAttributes<HTMLDivElement>>;
5
4
  export declare const SelectValue: import("react").ForwardRefExoticComponent<SelectPrimitive.SelectValueProps & import("react").RefAttributes<HTMLSpanElement>>;
5
+ /** Select root; disabled when there are no items to choose from. */
6
+ export declare function Select({ children, disabled, ...props }: ComponentPropsWithoutRef<typeof SelectPrimitive.Root>): import("react").JSX.Element;
6
7
  export interface SelectTriggerProps extends ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
7
8
  /** `field` fills a form; `inline` mimics the compact app dropdown. */
8
9
  variant?: "field" | "inline";
@@ -1 +1 @@
1
- {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../src/select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,OAAO,CAAC;AAIf,eAAO,MAAM,MAAM,iDAAuB,CAAC;AAC3C,eAAO,MAAM,WAAW,6HAAwB,CAAC;AACjD,eAAO,MAAM,WAAW,8HAAwB,CAAC;AAEjD,MAAM,WAAW,kBAAmB,SAAQ,wBAAwB,CAClE,OAAO,eAAe,CAAC,OAAO,CAC/B;IACC,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,kHAuBxB,CAAC;AAGH,eAAO,MAAM,aAAa,4LA+BzB,CAAC;AAGF,eAAO,MAAM,UAAU,yLAmBrB,CAAC;AAGH,eAAO,MAAM,WAAW,0LAStB,CAAC"}
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../src/select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,OAAO,CAAC;AAKf,eAAO,MAAM,WAAW,6HAAwB,CAAC;AACjD,eAAO,MAAM,WAAW,8HAAwB,CAAC;AAEjD,oEAAoE;AACpE,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,wBAAwB,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,+BAQvD;AAED,MAAM,WAAW,kBAAmB,SAAQ,wBAAwB,CAClE,OAAO,eAAe,CAAC,OAAO,CAC/B;IACC,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,kHAwBxB,CAAC;AAGH,eAAO,MAAM,aAAa,4LA+BzB,CAAC;AAGF,eAAO,MAAM,UAAU,yLAmBrB,CAAC;AAGH,eAAO,MAAM,WAAW,0LAStB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinaan/uikit",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Reusable React and Tailwind UI components for Jinaan projects.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -14,7 +14,7 @@ interface AgentPromptProps {
14
14
  }
15
15
 
16
16
  /**
17
- * The "Add Clerk auth to my app" prompt pill: a dark tag overlapping a
17
+ * The "Add Jinaan auth to my app" prompt pill: a dark tag overlapping a
18
18
  * rounded command field with a copy button.
19
19
  */
20
20
  export function AgentPrompt({
@@ -153,7 +153,7 @@ function initials(name: string): string {
153
153
  .join("");
154
154
  }
155
155
 
156
- /** Clerk-style identity cell: avatar + primary name + secondary line. */
156
+ /** Jinaan-style identity cell: avatar + primary name + secondary line. */
157
157
  export function DataTableIdentity({
158
158
  name,
159
159
  secondary,
@@ -1,15 +1,52 @@
1
1
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
2
  import { Check } from "@phosphor-icons/react";
3
3
  import {
4
+ createContext,
4
5
  forwardRef,
6
+ useContext,
5
7
  type ComponentPropsWithoutRef,
6
8
  type ElementRef,
7
9
  } from "react";
8
10
 
11
+ import { countDescendants } from "./item-count";
9
12
  import { cn } from "./utils";
10
13
 
11
- export const DropdownMenu = DropdownMenuPrimitive.Root;
12
- export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
14
+ const DropdownMenuEmptyContext = createContext(false);
15
+
16
+ /** Menu root; disables its trigger when there are no items. */
17
+ export function DropdownMenu({
18
+ children,
19
+ ...props
20
+ }: ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>) {
21
+ const empty =
22
+ countDescendants(children, [
23
+ DropdownMenuItem,
24
+ DropdownMenuCheckboxItem,
25
+ ]) === 0;
26
+
27
+ return (
28
+ <DropdownMenuEmptyContext.Provider value={empty}>
29
+ <DropdownMenuPrimitive.Root {...props}>{children}</DropdownMenuPrimitive.Root>
30
+ </DropdownMenuEmptyContext.Provider>
31
+ );
32
+ }
33
+
34
+ export const DropdownMenuTrigger = forwardRef<
35
+ ElementRef<typeof DropdownMenuPrimitive.Trigger>,
36
+ ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>
37
+ >(({ disabled, ...props }, ref) => {
38
+ const empty = useContext(DropdownMenuEmptyContext);
39
+
40
+ return (
41
+ <DropdownMenuPrimitive.Trigger
42
+ ref={ref}
43
+ disabled={disabled || empty}
44
+ {...props}
45
+ />
46
+ );
47
+ });
48
+ DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
49
+
13
50
  export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
14
51
  export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
15
52
 
@@ -0,0 +1,21 @@
1
+ import { Children, isValidElement, type ReactNode } from "react";
2
+
3
+ /** Count matching elements anywhere in a static React children tree. */
4
+ export function countDescendants(
5
+ children: ReactNode,
6
+ types: readonly unknown[],
7
+ ): number {
8
+ let count = 0;
9
+
10
+ Children.forEach(children, (child) => {
11
+ if (!isValidElement(child)) return;
12
+ if (types.some((type) => type === child.type)) {
13
+ count += 1;
14
+ return;
15
+ }
16
+ const nested = (child.props as { children?: ReactNode }).children;
17
+ if (nested) count += countDescendants(nested, types);
18
+ });
19
+
20
+ return count;
21
+ }
package/src/segmented.tsx CHANGED
@@ -18,7 +18,7 @@ interface SegmentedProps<T extends string> {
18
18
  }
19
19
 
20
20
  /**
21
- * Single-choice control for a small set of options, styled after the Clerk
21
+ * Single-choice control for a small set of options, styled after the Jinaan
22
22
  * dashboard: a recessed track with an elevated selected pill. Implemented as a
23
23
  * `radiogroup` of native radios, so arrow-key roving and forms work natively.
24
24
  */
package/src/select.tsx CHANGED
@@ -6,12 +6,27 @@ import {
6
6
  type ElementRef,
7
7
  } from "react";
8
8
 
9
+ import { countDescendants } from "./item-count";
9
10
  import { cn } from "./utils";
10
11
 
11
- export const Select = SelectPrimitive.Root;
12
12
  export const SelectGroup = SelectPrimitive.Group;
13
13
  export const SelectValue = SelectPrimitive.Value;
14
14
 
15
+ /** Select root; disabled when there are no items to choose from. */
16
+ export function Select({
17
+ children,
18
+ disabled,
19
+ ...props
20
+ }: ComponentPropsWithoutRef<typeof SelectPrimitive.Root>) {
21
+ const empty = countDescendants(children, [SelectItem]) === 0;
22
+
23
+ return (
24
+ <SelectPrimitive.Root disabled={disabled || empty} {...props}>
25
+ {children}
26
+ </SelectPrimitive.Root>
27
+ );
28
+ }
29
+
15
30
  export interface SelectTriggerProps extends ComponentPropsWithoutRef<
16
31
  typeof SelectPrimitive.Trigger
17
32
  > {
@@ -28,11 +43,12 @@ export const SelectTrigger = forwardRef<
28
43
  className={cn(
29
44
  "flex items-center justify-between gap-2 text-sm text-fg",
30
45
  "focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
46
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
31
47
  "[&>span]:truncate",
32
48
  variant === "field" &&
33
- "h-9 w-full rounded-md border border-line bg-surface px-3 focus:border-primary focus:ring-2 focus:ring-focus/30",
49
+ "h-(--control-h-md) w-full rounded-md border border-line bg-surface px-3 focus:border-primary focus:ring-2 focus:ring-focus/30",
34
50
  variant === "inline" &&
35
- "btn-fill btn-inner-border btn-interactive h-8 w-auto rounded-(--control-radius) border-0 bg-(--button-secondary-bg) px-(--control-pad-x-sm) font-medium text-fg hover:bg-(--button-surface-hover) active:bg-(--button-surface-active) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-strong",
51
+ "btn-fill btn-inner-border btn-interactive h-(--control-h-md) w-auto rounded-(--control-radius) border-0 bg-(--button-secondary-bg) px-(--control-pad-x-sm) font-medium text-fg hover:bg-(--button-surface-hover) active:bg-(--button-surface-active) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-strong",
36
52
  className,
37
53
  )}
38
54
  {...props}
package/styles.css CHANGED
@@ -8,10 +8,10 @@
8
8
  /*
9
9
  * Semantic design tokens. Components must reference these (via the Tailwind
10
10
  * utilities below) rather than raw palette values, so both themes stay correct.
11
- * Values mirror the Clerk "Ceramic" system captured in docs/design-reference.md.
11
+ * Values mirror the Jinaan "Ceramic" system captured in docs/design-reference.md.
12
12
  */
13
13
  :root {
14
- /* Clerk shell: white page, gray-50 chrome/cards. */
14
+ /* Jinaan shell: white page, gray-50 chrome/cards. */
15
15
  --page: #ffffff;
16
16
  --canvas: #ffffff;
17
17
  --surface: #fafafb;
@@ -121,7 +121,7 @@
121
121
  /* Destructive keeps its red identity; hover/pressed are red-shifted. */
122
122
  --button-destructive-hover: color-mix(in oklab, var(--error) 88%, black);
123
123
  --button-destructive-active: color-mix(in oklab, var(--error) 78%, black);
124
- /* Filled buttons: flat surface + inset gloss + soft drop (Clerk). */
124
+ /* Filled buttons: flat surface + inset gloss + soft drop (Jinaan). */
125
125
  --button-inner: rgb(255 255 255 / 0.45);
126
126
  /* Primary and destructive read their own, stronger top gloss. */
127
127
  --button-primary-inner: rgb(255 255 255 / 0.5);
@@ -140,7 +140,7 @@
140
140
  0 10px 28px -14px rgb(19 19 22 / 0.2);
141
141
  --dialog-noise-opacity: 0.04;
142
142
 
143
- /* Settings danger-zone shell (Clerk red-tinted surface). */
143
+ /* Settings danger-zone shell (Jinaan red-tinted surface). */
144
144
  --settings-danger-bg: #fff5f4;
145
145
 
146
146
  /* Third-party brand marks (identity providers). */
@@ -155,7 +155,7 @@
155
155
  }
156
156
 
157
157
  .dark {
158
- /* Clerk shell: page gray-1400, chrome/cards gray-1500. */
158
+ /* Jinaan shell: page gray-1400, chrome/cards gray-1500. */
159
159
  --page: #19191c;
160
160
  --canvas: #1b1b1f;
161
161
  --surface: #111113;
@@ -205,7 +205,7 @@
205
205
  --table-row-selected: #2b2b34;
206
206
  --table-accent: var(--primary);
207
207
 
208
- /* Control system overrides (dark) — surfaces match the Clerk dashboard. */
208
+ /* Control system overrides (dark) — surfaces match the Jinaan dashboard. */
209
209
  --input-bg: #30303a;
210
210
  --input-fg: #f4f4f5;
211
211
  --input-placeholder: #777783;
@@ -324,7 +324,7 @@
324
324
  @layer components {
325
325
  /*
326
326
  * Filled control surface: a soft blurred drop plus a 1px inset top gloss
327
- * (matches the Clerk dashboard buttons). The Tailwind focus ring replaces
327
+ * (matches the Jinaan dashboard buttons). The Tailwind focus ring replaces
328
328
  * this shadow while focused.
329
329
  */
330
330
  .btn-fill {