@kala-ui/react-app 0.1.0-beta.1 → 0.1.0-beta.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @kala-ui/react-app
2
2
 
3
+ ## 0.1.0-beta.3
4
+
5
+ ### Minor Changes
6
+
7
+ - NavLink: optional anchor mode. Passing `href` renders a real `<a>` (default button behavior unchanged otherwise) so top-level navigation targets keep native middle-click/clone-tab semantics. In anchor mode: `active` sets `aria-current="page"`; button-only props (`type`, `disabled`) are never spread onto the anchor (`disabled` degrades to `aria-disabled` + pointer-events styling); a plain left-click is preventDefaulted for client-side routing while modifier/middle clicks stay native. Nested-collapse (chevron toggle) remains button-mode-only.
8
+
9
+ ## 0.1.0-beta.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+ - @kala-ui/react@0.1.0-beta.2
15
+
3
16
  ## 0.1.0-beta.1
4
17
 
5
18
  ### Minor Changes
@@ -1,5 +1,5 @@
1
1
  import type * as React from "react";
2
- export interface NavLinkProps extends Omit<React.ComponentProps<"button">, "onChange"> {
2
+ export interface NavLinkProps extends Omit<React.ComponentProps<"button">, "onChange" | "ref"> {
3
3
  /** Link label */
4
4
  label: React.ReactNode;
5
5
  /** Link description */
@@ -10,6 +10,15 @@ export interface NavLinkProps extends Omit<React.ComponentProps<"button">, "onCh
10
10
  rightSection?: React.ReactNode;
11
11
  /** Active state */
12
12
  active?: boolean;
13
+ /**
14
+ * Renders the NavLink as a real anchor (<a href>) instead of a button.
15
+ * Use for top-level navigation targets: the href keeps middle-click,
16
+ * clone-tab and keyboard semantics native. A plain left-click is
17
+ * preventDefaulted so SPA routers can route client-side; modifier clicks
18
+ * are left to the browser. Nested-collapse behavior (children chevron
19
+ * toggle) only applies in button mode.
20
+ */
21
+ href?: string;
13
22
  /** Collapsed/Expanded state for nested items */
14
23
  defaultOpen?: boolean;
15
24
  /** Controlled opened state */
@@ -23,4 +32,4 @@ export interface NavLinkProps extends Omit<React.ComponentProps<"button">, "onCh
23
32
  /** Indentation for nested items */
24
33
  indent?: boolean;
25
34
  }
26
- export declare function NavLink({ ref, className, label, description, icon, rightSection, active, defaultOpen, open: openProp, onOpenChange, children, disableRightSectionRotation, indent, onClick, ...props }: NavLinkProps): React.JSX.Element;
35
+ export declare function NavLink({ className, label, description, icon, rightSection, active, href, defaultOpen, open: openProp, onOpenChange, children, disableRightSectionRotation, indent, onClick, type, disabled, ...props }: NavLinkProps): React.JSX.Element;
@@ -3,12 +3,45 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
3
3
  import { cn } from "@kala-ui/react/lib/utils";
4
4
  import { useUncontrolled } from "@kala-ui/react-hooks";
5
5
  import { ChevronRight } from "lucide-react";
6
- export function NavLink({ ref, className, label, description, icon, rightSection, active, defaultOpen = false, open: openProp, onOpenChange, children, disableRightSectionRotation = false, indent = false, onClick, ...props }) {
6
+ /** Props shared by both arms with element-specific handlers widened to a
7
+ * common HTMLElement base, so the same rest-object spreads onto <button>
8
+ * and <a> without fighting their event-target variance. */
9
+ // biome-ignore lint: manual widening of every *EventHandler — Record spread is
10
+ // the pragmatic bridge until the props are re-modeled per-element.
11
+ const widenHandlers = (props) => {
12
+ const out = {};
13
+ for (const [key, value] of Object.entries(props)) {
14
+ out[key] =
15
+ typeof value === "function" && (key.startsWith("on") || key === "ref")
16
+ ? value
17
+ : value;
18
+ }
19
+ return out;
20
+ };
21
+ export function NavLink({ className, label, description, icon, rightSection, active, href, defaultOpen = false, open: openProp, onOpenChange, children, disableRightSectionRotation = false, indent = false, onClick, type, disabled, ...props }) {
7
22
  const [isOpened, handleOpenChange] = useUncontrolled({
8
23
  value: openProp,
9
24
  defaultValue: defaultOpen,
10
25
  onChange: onOpenChange,
11
26
  });
27
+ const content = (_jsxs(_Fragment, { children: [icon && (_jsx("span", { className: "flex items-center justify-center", children: icon })), _jsxs("div", { className: "flex flex-1 flex-col items-start overflow-hidden", children: [_jsx("span", { className: "truncate", children: label }), description && (_jsx("span", { className: "truncate text-xs text-muted-foreground font-normal", children: description }))] }), (rightSection || children) && (_jsx("span", { className: cn("flex items-center justify-center text-muted-foreground transition-transform duration-200", children &&
28
+ !disableRightSectionRotation &&
29
+ isOpened &&
30
+ "rotate-90"), children: rightSection || (children && _jsx(ChevronRight, { className: "h-4 w-4" })) }))] }));
31
+ const baseClassName = cn("flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", active ? "bg-accent text-accent-foreground" : "text-muted-foreground", className);
32
+ if (href) {
33
+ // Anchor mode: button-only props (type/disabled) are invalid on <a> —
34
+ // disabled degrades to aria-disabled + pointer-events styling. Plain
35
+ // left-clicks are the SPA router's to handle; every other click
36
+ // (modifiers, middle) stays native so open-in-new-tab keeps working.
37
+ const handleAnchorClick = (e) => {
38
+ const isPlainLeftClick = e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
39
+ if (isPlainLeftClick)
40
+ e.preventDefault();
41
+ onClick?.(e);
42
+ };
43
+ return (_jsx("a", { "data-kala-component": "nav-link", href: href, "aria-current": active ? "page" : undefined, "aria-disabled": disabled || undefined, className: cn(baseClassName, disabled && "pointer-events-none opacity-50"), onClick: handleAnchorClick, ...widenHandlers(props), children: content }));
44
+ }
12
45
  const handleToggle = (e) => {
13
46
  if (children) {
14
47
  e.preventDefault();
@@ -16,8 +49,5 @@ export function NavLink({ ref, className, label, description, icon, rightSection
16
49
  }
17
50
  onClick?.(e);
18
51
  };
19
- return (_jsxs(_Fragment, { children: [_jsxs("button", { "data-kala-component": "nav-link", ref: ref, className: cn("flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", active ? "bg-accent text-accent-foreground" : "text-muted-foreground", className), onClick: handleToggle, ...props, children: [icon && (_jsx("span", { className: "flex items-center justify-center", children: icon })), _jsxs("div", { className: "flex flex-1 flex-col items-start overflow-hidden", children: [_jsx("span", { className: "truncate", children: label }), description && (_jsx("span", { className: "truncate text-xs text-muted-foreground font-normal", children: description }))] }), (rightSection || children) && (_jsx("span", { className: cn("flex items-center justify-center text-muted-foreground transition-transform duration-200", children &&
20
- !disableRightSectionRotation &&
21
- isOpened &&
22
- "rotate-90"), children: rightSection || (children && _jsx(ChevronRight, { className: "h-4 w-4" })) }))] }), children && isOpened && (_jsx("div", { className: cn("flex flex-col gap-1", indent && "pl-4"), children: children }))] }));
52
+ return (_jsxs(_Fragment, { children: [_jsx("button", { "data-kala-component": "nav-link", type: type ?? "button", disabled: disabled, className: baseClassName, onClick: handleToggle, ...widenHandlers(props), children: content }), children && isOpened && (_jsx("div", { className: cn("flex flex-col gap-1", indent && "pl-4"), children: children }))] }));
23
53
  }
@@ -118,6 +118,9 @@
118
118
  }
119
119
  }
120
120
  @layer utilities {
121
+ .pointer-events-none {
122
+ pointer-events: none;
123
+ }
121
124
  .collapse {
122
125
  visibility: collapse;
123
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kala-ui/react-app",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -126,8 +126,8 @@
126
126
  "lucide-react": "~1.34.0",
127
127
  "react-apexcharts": "~2.1.1",
128
128
  "simple-icons": "~16.28.0",
129
- "@kala-ui/react-hooks": "0.1.0-beta.0",
130
- "@kala-ui/react": "0.1.0-beta.1"
129
+ "@kala-ui/react": "0.1.0-beta.2",
130
+ "@kala-ui/react-hooks": "0.1.0-beta.0"
131
131
  },
132
132
  "devDependencies": {
133
133
  "@tailwindcss/postcss": "~4.3.3",