@knymbus/voxel-ui 1.0.15 → 1.0.19

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.
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "../../chunks/search-COxH7vvi.js";
2
- export { t as SearchInput, e as searchTokens };
1
+ import { t as e } from "../../chunks/search-DSDSn6PK.js";
2
+ export { e as SearchInput };
@@ -20,23 +20,3 @@ export interface SearchInputProps {
20
20
  showFloatPeek?: boolean;
21
21
  peekContent?: React.ReactNode;
22
22
  }
23
- export declare const searchTokens: {
24
- colors: {
25
- bgInput: string;
26
- border: string;
27
- borderAccent: string;
28
- borderAccentHover: string;
29
- text: string;
30
- muted: string;
31
- sidebarBg: string;
32
- hoverBg: string;
33
- accentBgLight: string;
34
- };
35
- typography: {
36
- sans: string;
37
- mono: string;
38
- };
39
- transitions: {
40
- smooth: string;
41
- };
42
- };
@@ -1,2 +1,2 @@
1
- import { i as e, n as t, r as n, t as r } from "../../chunks/tabs-MaVN00hJ.js";
1
+ import { i as e, n as t, r as n, t as r } from "../../chunks/tabs-DkmFedCI.js";
2
2
  export { r as TabButton, t as TabPanel, n as TabPanelList, e as useTab };
@@ -0,0 +1,20 @@
1
+ export declare const searchTokens: {
2
+ colors: {
3
+ bgInput: string;
4
+ border: string;
5
+ borderAccent: string;
6
+ borderAccentHover: string;
7
+ text: string;
8
+ muted: string;
9
+ sidebarBg: string;
10
+ hoverBg: string;
11
+ accentBgLight: string;
12
+ };
13
+ typography: {
14
+ sans: string;
15
+ mono: string;
16
+ };
17
+ transitions: {
18
+ smooth: string;
19
+ };
20
+ };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as e } from "./chunks/resizable-ImB8dfG_.js";
2
- import { i as t, n, r, t as i } from "./chunks/tabs-MaVN00hJ.js";
2
+ import { i as t, n, r, t as i } from "./chunks/tabs-DkmFedCI.js";
3
3
  import { t as a } from "./chunks/Button-BgQwvn3C.js";
4
4
  import { n as o, t as s } from "./chunks/button-dHcpTNIG.js";
5
5
  import { C as c, E as l, S as u, T as d, _ as f, a as p, b as m, c as h, d as g, f as _, g as v, h as y, i as b, l as x, m as S, n as C, o as w, p as T, r as E, s as D, t as O, u as k, v as A, w as j, x as M, y as N } from "./chunks/icons-BpfDVwCQ.js";
6
- import { n as P, t as F } from "./chunks/search-COxH7vvi.js";
7
- export { A as Add, S as BlankDoc, a as Button, o as ButtonGroup, _ as Chat, N as ChevronDown, l as Close, x as Comment, k as DeleteChat, j as Document, v as Expand, c as Folder, C as Group, y as Minimize, f as Minus, w as More, D as OpenFolder, b as Person, g as PlusChat, h as PlusComment, T as PlusDoc, O as PlusDocBadge, E as PlusPerson, M as Refresh, e as ResizablePanel, u as Search, F as SearchInput, s as SplitActionButton, i as TabButton, n as TabPanel, r as TabPanelList, d as Terminal, m as Trash, p as Truck, P as searchTokens, t as useTab };
6
+ import { t as P } from "./chunks/search-DSDSn6PK.js";
7
+ export { A as Add, S as BlankDoc, a as Button, o as ButtonGroup, _ as Chat, N as ChevronDown, l as Close, x as Comment, k as DeleteChat, j as Document, v as Expand, c as Folder, C as Group, y as Minimize, f as Minus, w as More, D as OpenFolder, b as Person, g as PlusChat, h as PlusComment, T as PlusDoc, O as PlusDocBadge, E as PlusPerson, M as Refresh, e as ResizablePanel, u as Search, P as SearchInput, s as SplitActionButton, i as TabButton, n as TabPanel, r as TabPanelList, d as Terminal, m as Trash, p as Truck, t as useTab };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knymbus/voxel-ui",
3
- "version": "1.0.15",
3
+ "version": "1.0.19",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import SearchInput from './SearchInput';
4
4
  import { SearchResultItem } from './types';
@@ -45,10 +45,10 @@ const MultiSearchWorkspace = () => {
45
45
  }
46
46
 
47
47
  return (
48
- <div style={{ fontFamily: 'var(--font-sans)', minWidth: '450px', backgroundColor: 'var(--color-vsc-sidebar)', border: '1px solid var(--color-vsc-border)', borderRadius: '6px', padding: '32px' }}>
48
+ <div className="p-8 bg-vsc-sidebar border border-vsc-border rounded-md space-y-8 min-w-112.5 text-vsc-text" >
49
49
 
50
50
  {/* 1. Simple Variant Bracket */}
51
- <div className="space-y-1.5" style={{ height: 'auto', width: '350px',}}>
51
+ <div className="space-y-1.5" >
52
52
  <p className="text-[10px] font-bold text-vsc-muted uppercase tracking-wider">1. Simple Matrix Variant with Count Log</p>
53
53
  <SearchInput
54
54
  variant="simple"
@@ -56,7 +56,7 @@ const MultiSearchWorkspace = () => {
56
56
  onChange={setSimpleQuery}
57
57
  resultsCount={filteredSimpleResults.length}
58
58
  placeholder="Type track ID to evaluate records count..."
59
- // peekContent={<ResultPanel />}
59
+ peekContent={<ResultPanel />}
60
60
  />
61
61
  </div>
62
62
 
@@ -1,8 +1,9 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
- import { SearchInputProps, searchTokens } from './types';
2
+ import { SearchInputProps } from './types';
3
3
  import { Search, Close } from '../icons';
4
4
  import { Button } from '../button/Button';
5
5
  import SearchMenu from './SearchMenu';
6
+ import { searchTokens } from '../types';
6
7
 
7
8
  export default function SearchInput({
8
9
  variant = 'simple',
@@ -159,13 +160,13 @@ export default function SearchInput({
159
160
  zIndex: 10,
160
161
  // overflow: 'hidden',
161
162
  transition: searchTokens.transitions.smooth,
162
- height: hasResults || hasQuery ? 'auto' : '0px',
163
- opacity: hasResults || hasQuery ? 1 : 0,
164
- transform: hasResults || hasQuery ? 'translateY(0px)' : 'translateY(-4px)',
163
+ height: hasQuery ? 'auto' : '0px',
164
+ opacity: hasQuery ? 1 : 0,
165
+ transform: hasQuery ? 'translateY(4px)' : 'translateY(-4px)',
165
166
  pointerEvents: 'none'
166
167
  }}
167
168
  >
168
- <div style={{ backgroundColor: searchTokens.colors.sidebarBg, border: `0px solid ${searchTokens.colors.border}`, borderTop: 'none', padding: '4px 6px', borderRadius: '0 0 6px 6px', boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1)' }}>
169
+ <div style={{ backgroundColor: searchTokens.colors.sidebarBg, border: `0px solid ${searchTokens.colors.border}`, borderTop: 'none', padding: '4px 6px', borderRadius: '6px', boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1)' }}>
169
170
  {renderedPeek()}
170
171
  </div>
171
172
  </div>
@@ -1,5 +1,6 @@
1
1
  import { useState } from 'react';
2
- import { SearchResultItem, searchTokens } from './types';
2
+ import { SearchResultItem } from './types';
3
+ import { searchTokens } from '../types';
3
4
 
4
5
  interface SearchMenuProps {
5
6
  isOpen: boolean;
@@ -25,24 +25,3 @@ export interface SearchInputProps {
25
25
  peekContent?: React.ReactNode;
26
26
  }
27
27
 
28
- export const searchTokens = {
29
- colors: {
30
- bgInput: 'var(--color-vsc-bg-input, #f6f8fa)',
31
- border: 'var(--color-vsc-border, #e4e4e7)',
32
- borderAccent: 'var(--color-vsc-accent, #007acc)',
33
- borderAccentHover: 'var(--color-vsc-accent-hover, #0062a3)',
34
- text: 'var(--color-vsc-text, #333333)',
35
- muted: 'var(--color-vsc-muted, #6a737d)',
36
- sidebarBg: 'var(--color-vsc-sidebar, #f3f3f3)',
37
- hoverBg: 'var(--color-vsc-hover, #e8e8e8)',
38
- accentBgLight: 'rgba(0, 122, 204, 0.1)'
39
- },
40
- typography: {
41
- /* ⚡ Pulls Tailwind's native font configuration maps from your global layout shell App context */
42
- sans: 'var(--font-sans, font-sans, ui-sans-serif, system-ui, sans-serif)',
43
- mono: 'var(--font-mono, font-mono, ui-monospace, SFMono-Regular, monospace)'
44
- },
45
- transitions: {
46
- smooth: 'all 200ms cubic-bezier(0.34, 1.56, 0.64, 1)'
47
- }
48
- };
@@ -1,6 +1,7 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { useTab } from './useTab';
3
- import { TabVariant } from './types';
3
+ import { TabVariant } from './types'; // Shares the typography/color tokens mapping schema
4
+ import { searchTokens } from '../types';
4
5
 
5
6
  interface TabButtonProps {
6
7
  id: string;
@@ -20,37 +21,78 @@ export default function TabButton({
20
21
  endIcon
21
22
  }: TabButtonProps) {
22
23
  const { activeTab, changeTab } = useTab(scopeName);
24
+ const [isHovered, setIsHovered] = useState(false);
23
25
  const isCurrent = activeTab === id;
24
26
 
25
- const baseStyles = "flex items-center justify-center gap-1.5 focus:outline-none border-none outline-none z-10 transition-colors select-none h-full";
27
+ const baseButtonStyles: React.CSSProperties = {
28
+ display: 'inline-flex',
29
+ alignItems: 'center',
30
+ justifyContent: 'center',
31
+ gap: '6px',
32
+ backgroundColor: 'transparent',
33
+ border: 'none',
34
+ outline: 'none',
35
+ fontFamily: searchTokens.typography.sans,
36
+ fontSize: '12px',
37
+ userSelect: 'none',
38
+ cursor: 'pointer',
39
+ height: '100%',
40
+ transition: 'all 150ms ease',
41
+ boxSizing: 'border-box',
42
+ position: 'relative',
43
+ zIndex: 10
44
+ };
26
45
 
27
- const variantClasses: Record<TabVariant, string> = {
28
- underline: `pb-2 pt-1 px-3 text-xs font-semibold cursor-pointer border-b-2 ${
29
- isCurrent ? 'border-vsc-accent text-vsc-text' : 'border-transparent text-vsc-muted hover:text-vsc-text'
30
- }`,
31
- 'sliding-underline': `py-2 px-4 text-xs font-semibold cursor-pointer transition-colors duration-200 ${
32
- isCurrent ? 'text-vsc-text font-bold' : 'text-vsc-muted hover:text-vsc-text'
33
- }`,
34
- pill: `px-3.5 py-1.5 text-xs font-bold rounded-full cursor-pointer scale-100 ${
35
- isCurrent ? 'bg-vsc-accent text-vsc-button-text shadow-sm' : 'bg-vsc-hover/40 text-vsc-muted hover:bg-vsc-hover'
36
- }`,
37
- vscode: `px-4 text-xs font-medium border-r border-vsc-border cursor-pointer ${
38
- isCurrent ? 'bg-vsc-bg text-vsc-text border-t-2 border-t-vsc-accent -mt-[1px]' : 'bg-vsc-sidebar text-vsc-muted hover:bg-vsc-hover/50'
39
- }`,
40
- ghost: `px-3 py-1.5 text-xs font-semibold rounded cursor-pointer ${
41
- isCurrent ? 'bg-vsc-hover text-vsc-text font-bold' : 'text-vsc-muted hover:bg-vsc-hover/30'
42
- }`
46
+ const variantStyles: Record<TabVariant, React.CSSProperties> = {
47
+ underline: {
48
+ padding: '4px 12px 8px 12px',
49
+ fontWeight: isCurrent ? 600 : 500,
50
+ color: isCurrent ? searchTokens.colors.text : (isHovered ? searchTokens.colors.text : searchTokens.colors.muted),
51
+ borderBottom: `2px solid ${isCurrent ? searchTokens.colors.borderAccent : 'transparent'}`
52
+ },
53
+ 'sliding-underline': {
54
+ padding: '8px 16px',
55
+ fontWeight: isCurrent ? 700 : 500,
56
+ color: isCurrent ? searchTokens.colors.text : (isHovered ? searchTokens.colors.text : searchTokens.colors.muted)
57
+ },
58
+ pill: {
59
+ padding: '6px 14px',
60
+ fontWeight: 700,
61
+ borderRadius: '9999px',
62
+ backgroundColor: isCurrent ? searchTokens.colors.borderAccent : (isHovered ? searchTokens.colors.hoverBg : 'transparent'),
63
+ color: isCurrent ? '#ffffff' : (isHovered ? searchTokens.colors.text : searchTokens.colors.muted),
64
+ transform: isCurrent ? 'scale(1.02)' : 'scale(1)',
65
+ boxShadow: isCurrent ? '0 1px 3px rgba(0,0,0,0.1)' : 'none'
66
+ },
67
+ vscode: {
68
+ padding: '0 16px',
69
+ fontWeight: 500,
70
+ color: isCurrent ? searchTokens.colors.text : (isHovered ? searchTokens.colors.text : searchTokens.colors.muted),
71
+ backgroundColor: isCurrent ? searchTokens.colors.bgInput : (isHovered ? 'rgba(0,0,0,0.02)' : searchTokens.colors.sidebarBg),
72
+ borderRight: `1px solid ${searchTokens.colors.border}`,
73
+ borderTop: isCurrent ? `2px solid ${searchTokens.colors.borderAccent}` : '2px solid transparent',
74
+ marginTop: isCurrent ? '-1px' : '0'
75
+ },
76
+ ghost: {
77
+ padding: '6px 12px',
78
+ fontWeight: isCurrent ? 700 : 500,
79
+ borderRadius: '2px',
80
+ backgroundColor: isCurrent ? searchTokens.colors.hoverBg : (isHovered ? 'rgba(0,0,0,0.04)' : 'transparent'),
81
+ color: isCurrent ? searchTokens.colors.text : (isHovered ? searchTokens.colors.text : searchTokens.colors.muted)
82
+ }
43
83
  };
44
84
 
45
85
  return (
46
86
  <button
47
87
  data-id={id}
48
88
  onClick={() => changeTab(id)}
49
- className={`${baseStyles} ${variantClasses[variant]}`}
89
+ onMouseEnter={() => setIsHovered(true)}
90
+ onMouseLeave={() => setIsHovered(false)}
91
+ style={{ ...baseButtonStyles, ...variantStyles[variant] }}
50
92
  >
51
- {startIcon && <span className="shrink-0 opacity-80">{startIcon}</span>}
52
- <span className="truncate">{children}</span>
53
- {endIcon && <span className="shrink-0 opacity-70">{endIcon}</span>}
93
+ {startIcon && <span style={{ display: 'inline-flex', flexShrink: 0, opacity: isCurrent || isHovered ? 1 : 0.7 }}>{startIcon}</span>}
94
+ <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{children}</span>
95
+ {endIcon && <span style={{ display: 'inline-flex', flexShrink: 0, opacity: isCurrent || isHovered ? 1 : 0.6 }}>{endIcon}</span>}
54
96
  </button>
55
97
  );
56
98
  }
@@ -1,7 +1,8 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
2
  import { useTab } from './useTab';
3
3
  import { TabVariant } from './types';
4
- import TabButton from './TabButton'; // Imported to execute type-checking audits
4
+ import TabButton from './TabButton';
5
+ import { searchTokens } from '../types';
5
6
 
6
7
  interface TabButtonGroupProps {
7
8
  children: React.ReactNode;
@@ -20,15 +21,10 @@ export default function TabButtonGroup({
20
21
  const [lineStyles, setLineStyles] = useState<React.CSSProperties>({ left: 0, width: 0, opacity: 0 });
21
22
  const [hoveredId, setHoveredId] = useState<string | null>(null);
22
23
 
23
- // --- STRICT CHILDREN SUB-COMPONENT VALIDATION ENGINE LOOP ---
24
24
  const validatedChildren = React.Children.map(children, (child) => {
25
25
  if (!React.isValidElement(child)) return null;
26
-
27
- // Verify if the child component reference maps directly to our TabButton signature
28
26
  if (child.type !== TabButton) {
29
- console.warn(
30
- `[Voxel UI Validation Warning]: TabButtonGroup scope "${scopeName}" only accepts sub-children elements of type <TabButton />. Ignored invalid component node.`
31
- );
27
+ console.warn(`[Voxel UI Warning]: TabButtonGroup only accepts children of type <TabButton />.`);
32
28
  return null;
33
29
  }
34
30
  return child;
@@ -45,18 +41,27 @@ export default function TabButtonGroup({
45
41
  left: `${targetElement.offsetLeft}px`,
46
42
  width: `${targetElement.offsetWidth}px`,
47
43
  opacity: 1,
48
- backgroundColor: hoveredId && hoveredId !== activeTab ? 'var(--color-vsc-text)' : 'var(--color-vsc-accent)'
44
+ backgroundColor: hoveredId && hoveredId !== activeTab ? searchTokens.colors.text : searchTokens.colors.borderAccent
49
45
  });
50
46
  }
51
47
  }, [activeTab, hoveredId, variant]);
52
48
 
53
- const containerClasses = {
54
- underline: 'flex items-center space-x-2 border-b border-vsc-border w-full bg-vsc-bg pb-[1px]',
55
- 'sliding-underline': 'relative flex items-center border-b border-vsc-border w-full bg-vsc-bg pb-[2px]',
56
- pill: 'flex items-center space-x-1.5 p-1 bg-vsc-sidebar rounded-full w-fit',
57
- vscode: 'flex items-center h-9 bg-vsc-sidebar border-b border-vsc-border w-full overflow-hidden',
58
- ghost: 'flex items-center space-x-1 p-1 bg-vsc-bg-input border border-vsc-border rounded'
59
- }[variant];
49
+ const baseContainerStyles: React.CSSProperties = {
50
+ display: 'flex',
51
+ alignItems: 'center',
52
+ fontFamily: searchTokens.typography.sans,
53
+ userSelect: 'none',
54
+ boxSizing: 'border-box',
55
+ position: 'relative'
56
+ };
57
+
58
+ const variantContainerStyles: Record<TabVariant, React.CSSProperties> = {
59
+ underline: { width: '100%', borderBottom: `1px solid ${searchTokens.colors.border}`, backgroundColor: searchTokens.colors.bgInput, gap: '8px', paddingBottom: '1px' },
60
+ 'sliding-underline': { width: '100%', borderBottom: `1px solid ${searchTokens.colors.border}`, backgroundColor: searchTokens.colors.bgInput, paddingBottom: '2px' },
61
+ pill: { width: 'max-content', padding: '4px', backgroundColor: searchTokens.colors.sidebarBg, borderRadius: '9999px', gap: '6px' },
62
+ vscode: { width: '100%', height: '36px', backgroundColor: searchTokens.colors.sidebarBg, borderBottom: `1px solid ${searchTokens.colors.border}`, overflowX: 'auto', overflowY: 'hidden' },
63
+ ghost: { width: '100%', padding: '4px', backgroundColor: searchTokens.colors.bgInput, border: `1px solid ${searchTokens.colors.border}`, borderRadius: '4px', gap: '4px' }
64
+ };
60
65
 
61
66
  return (
62
67
  <div
@@ -66,15 +71,22 @@ export default function TabButtonGroup({
66
71
  if (btn) setHoveredId(btn.getAttribute('data-id'));
67
72
  }}
68
73
  onMouseLeave={() => setHoveredId(null)}
69
- className={`${containerClasses} select-none`}
74
+ style={{ ...baseContainerStyles, ...variantContainerStyles[variant] }}
70
75
  >
71
- {/* Render exclusively the type-validated sub-nodes collection array */}
72
76
  {validatedChildren}
73
77
 
78
+ {/* Real-time Sliding Underline Track Line Component */}
74
79
  {variant === 'sliding-underline' && (
75
80
  <div
76
- className="absolute bottom-0 h-0.5 rounded-t transition-all duration-200 ease-out pointer-events-none z-20"
77
- style={lineStyles}
81
+ style={{
82
+ position: 'absolute',
83
+ bottom: 0,
84
+ height: '2px',
85
+ borderRadius: '2px 2px 0 0',
86
+ transition: 'all 200ms cubic-bezier(0.34, 1.56, 0.64, 1)',
87
+ zIndex: 20,
88
+ ...lineStyles
89
+ }}
78
90
  />
79
91
  )}
80
92
  </div>
@@ -10,7 +10,6 @@ interface TabPanelProps {
10
10
  export default function TabPanel({ children, id, persist = false }: TabPanelProps) {
11
11
  const { activeTabId } = useTabListContext();
12
12
  const isCurrent = activeTabId === id;
13
-
14
13
  const [hasRenderedOnce, setHasRenderedOnce] = useState<boolean>(false);
15
14
 
16
15
  useEffect(() => {
@@ -22,21 +21,40 @@ export default function TabPanel({ children, id, persist = false }: TabPanelProp
22
21
  if (!hasRenderedOnce) return null;
23
22
  if (!persist && !isCurrent) return null;
24
23
 
24
+ // Render the core layout canvas frame absolutely with zero layout shifts
25
+ const panelInlineStyles: React.CSSProperties = {
26
+ position: 'absolute',
27
+ top: 0,
28
+ left: 0,
29
+ right: 0,
30
+ bottom: 0,
31
+ width: '100%',
32
+ height: '100%',
33
+ /*
34
+ ⚡ FIXED VISIBILITY OVERRIDE:
35
+ Bypassed background color fills entirely to prevent canvas z-index clipping traps.
36
+ */
37
+ backgroundColor: 'transparent',
38
+ transition: 'opacity 250ms ease-out, transform 250ms cubic-bezier(0.34, 1.56, 0.64, 1)',
39
+ boxSizing: 'border-box',
40
+
41
+ // Smooth opacity fading animation matrices
42
+ opacity: isCurrent ? 1 : 0,
43
+ transform: isCurrent ? 'scale(1)' : 'scale(0.995)',
44
+ zIndex: isCurrent ? 10 : 0,
45
+ pointerEvents: isCurrent ? 'auto' : 'none',
46
+ visibility: isCurrent ? 'visible' : 'hidden'
47
+ };
48
+
25
49
  return (
26
- <div
27
- className={`absolute inset-0 w-full h-full bg-vsc-bg transition-all duration-300 ease-in-out ${
28
- isCurrent
29
- ? 'opacity-100 scale-100 z-10 pointer-events-auto visible'
30
- : 'opacity-0 scale-[0.99] z-0 pointer-events-none invisible delay-75'
31
- }`}
32
- >
33
- {/*
34
- Inner layout wrapper applies a micro-blur and fine opacity mask cross-fade
35
- to ensure background elements never clip through transparent gaps during transitions.
36
- */}
37
- <div className={`w-full h-full transition-opacity duration-300 ${
38
- isCurrent ? 'opacity-100' : 'opacity-0'
39
- }`}>
50
+ <div style={panelInlineStyles}>
51
+ <div
52
+ style={{
53
+ width: '100%',
54
+ height: '100%',
55
+ boxSizing: 'border-box'
56
+ }}
57
+ >
40
58
  {children}
41
59
  </div>
42
60
  </div>
@@ -1,10 +1,11 @@
1
1
  import React, { useEffect, createContext, useContext } from 'react';
2
2
  import { useTab } from './useTab';
3
+ import { searchTokens } from '../types';
3
4
 
4
5
  interface TabPanelListProps {
5
6
  children: React.ReactNode;
6
- targetScopeName: string; // Ties this structural list panel block to a named useTab hook loop
7
- defaultValue: string; // Initial focus viewport tab on render
7
+ targetScopeName: string; // Ties this list panel block to a named useTab instance
8
+ defaultValue: string; // Initial focused tab on mount
8
9
  }
9
10
 
10
11
  const TabListContext = createContext<{ activeTabId: string | undefined }>({ activeTabId: undefined });
@@ -14,16 +15,29 @@ export const useTabListContext = () => useContext(TabListContext);
14
15
  export default function TabPanelList({ children, targetScopeName, defaultValue }: TabPanelListProps) {
15
16
  const { activeTab, registerDefault } = useTab(targetScopeName);
16
17
 
17
- // Initialize defaultValue into state if it's the first execution pass
18
+ // Initialize defaultValue into the global Zustand store on first render execution
18
19
  useEffect(() => {
19
20
  registerDefault(defaultValue);
20
21
  }, [defaultValue, registerDefault]);
21
22
 
22
23
  const activeTabId = activeTab || defaultValue;
23
24
 
25
+ // Root canvas frame styling object using pure inline layout logic
26
+ const listContainerInlineStyles: React.CSSProperties = {
27
+ position: 'relative',
28
+ width: '100%',
29
+ height: '100%',
30
+ minHeight: 0,
31
+ flex: '1 1 0%',
32
+ overflow: 'hidden',
33
+ backgroundColor: searchTokens.colors.bgInput,
34
+ fontFamily: searchTokens.typography.sans,
35
+ boxSizing: 'border-box'
36
+ };
37
+
24
38
  return (
25
39
  <TabListContext.Provider value={{ activeTabId }}>
26
- <div className="relative w-full h-full min-h-0 flex-1 overflow-hidden bg-vsc-bg">
40
+ <div style={listContainerInlineStyles}>
27
41
  {children}
28
42
  </div>
29
43
  </TabListContext.Provider>
@@ -0,0 +1,22 @@
1
+
2
+ export const searchTokens = {
3
+ colors: {
4
+ bgInput: 'var(--color-vsc-bg-input, #f6f8fa)',
5
+ border: 'var(--color-vsc-border, #e4e4e7)',
6
+ borderAccent: 'var(--color-vsc-accent, #007acc)',
7
+ borderAccentHover: 'var(--color-vsc-accent-hover, #0062a3)',
8
+ text: 'var(--color-vsc-text, #333333)',
9
+ muted: 'var(--color-vsc-muted, #6a737d)',
10
+ sidebarBg: 'var(--color-vsc-sidebar, #f3f3f3)',
11
+ hoverBg: 'var(--color-vsc-hover, #e8e8e8)',
12
+ accentBgLight: 'rgba(0, 122, 204, 0.1)'
13
+ },
14
+ typography: {
15
+ /* ⚡ Pulls Tailwind's native font configuration maps from your global layout shell App context */
16
+ sans: 'var(--font-sans, font-sans, ui-sans-serif, system-ui, sans-serif)',
17
+ mono: 'var(--font-mono, font-mono, ui-monospace, SFMono-Regular, monospace)'
18
+ },
19
+ transitions: {
20
+ smooth: 'all 200ms cubic-bezier(0.34, 1.56, 0.64, 1)'
21
+ }
22
+ };
package/src/index.css CHANGED
@@ -36,4 +36,12 @@
36
36
  --color-vsc-muted: #8b949e;
37
37
  --color-vsc-bg-input: #1f1f1f;
38
38
  }
39
+ }
40
+
41
+ .p-8{
42
+ padding:32px;
43
+ }
44
+
45
+ .voxel:bg-amber-500{
46
+ background-color:var(--color-vsc-accent);
39
47
  }
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- import './index.css';
2
1
  export * from './components/resizable';
3
2
  export * from './components/tabs';
4
3
  export * from './components/button';
package/vite.config.mts CHANGED
@@ -8,12 +8,11 @@ import { fileURLToPath } from 'node:url';
8
8
  import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
9
9
  import { playwright } from '@vitest/browser-playwright';
10
10
  const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
11
- import tailwindcss from '@tailwindcss/vite';
11
+
12
12
 
13
13
  // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
14
14
  export default defineConfig({
15
15
  plugins: [
16
- tailwindcss(),
17
16
  react(),
18
17
  // Auto-generates independent TypeScript type declaration files (.d.ts) matching paths
19
18
  dts({
@@ -1,3 +0,0 @@
1
- /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
2
- @layer theme{:root,:host{--voxel-font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--voxel-font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--voxel-default-font-family:var(--voxel-font-sans);--voxel-default-mono-font-family:var(--voxel-font-mono)}}@layer base{@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--voxel-default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--voxel-default-font-feature-settings,normal);font-variation-settings:var(--voxel-default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--voxel-default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--voxel-default-mono-font-feature-settings,normal);font-variation-settings:var(--voxel-default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}.dark{--color-vsc-bg:#1e1e1e;--color-vsc-sidebar:#252526;--color-vsc-border:#3c3c3c;--color-vsc-accent:#007acc;--color-vsc-accent-hover:#0062a3;--color-vsc-hover:#2a2d2e;--color-vsc-text:#ccc;--color-vsc-muted:#8b949e;--color-vsc-bg-input:#1f1f1f}}@layer components,utilities;
3
- /*$vite$:1*/
@@ -1,86 +0,0 @@
1
- import { t as e } from "./jsx-runtime-Boo2vksn.js";
2
- import { createContext as t, useContext as n, useEffect as r, useState as i } from "react";
3
- import { create as a } from "zustand";
4
- //#region src/components/tabs/useTab.ts
5
- var o = a((e) => ({
6
- activeTabs: {},
7
- setActiveTab: (t, n) => e((e) => ({ activeTabs: {
8
- ...e.activeTabs,
9
- [t]: n
10
- } })),
11
- initializeTab: (t, n) => e((e) => e.activeTabs[t] ? {} : { activeTabs: {
12
- ...e.activeTabs,
13
- [t]: n
14
- } })
15
- }));
16
- function s(e) {
17
- let t = o((t) => t.activeTabs[e]), n = o((e) => e.setActiveTab), r = o((e) => e.initializeTab);
18
- return {
19
- activeTab: t,
20
- changeTab: (t) => n(e, t),
21
- registerDefault: (t) => r(e, t)
22
- };
23
- }
24
- //#endregion
25
- //#region src/components/tabs/TabPanelList.tsx
26
- var c = e(), l = t({ activeTabId: void 0 }), u = () => n(l);
27
- function d({ children: e, targetScopeName: t, defaultValue: n }) {
28
- let { activeTab: i, registerDefault: a } = s(t);
29
- r(() => {
30
- a(n);
31
- }, [n, a]);
32
- let o = i || n;
33
- return /* @__PURE__ */ (0, c.jsx)(l.Provider, {
34
- value: { activeTabId: o },
35
- children: /* @__PURE__ */ (0, c.jsx)("div", {
36
- className: "relative w-full h-full min-h-0 flex-1 overflow-hidden bg-vsc-bg",
37
- children: e
38
- })
39
- });
40
- }
41
- //#endregion
42
- //#region src/components/tabs/TabPanel.tsx
43
- function f({ children: e, id: t, persist: n = !1 }) {
44
- let { activeTabId: a } = u(), o = a === t, [s, l] = i(!1);
45
- return r(() => {
46
- o && !s && l(!0);
47
- }, [o, s]), !s || !n && !o ? null : /* @__PURE__ */ (0, c.jsx)("div", {
48
- className: `absolute inset-0 w-full h-full bg-vsc-bg transition-all duration-300 ease-in-out ${o ? "opacity-100 scale-100 z-10 pointer-events-auto visible" : "opacity-0 scale-[0.99] z-0 pointer-events-none invisible delay-75"}`,
49
- children: /* @__PURE__ */ (0, c.jsx)("div", {
50
- className: `w-full h-full transition-opacity duration-300 ${o ? "opacity-100" : "opacity-0"}`,
51
- children: e
52
- })
53
- });
54
- }
55
- //#endregion
56
- //#region src/components/tabs/TabButton.tsx
57
- function p({ id: e, scopeName: t, children: n, variant: r = "underline", startIcon: i, endIcon: a }) {
58
- let { activeTab: o, changeTab: l } = s(t), u = o === e;
59
- return /* @__PURE__ */ (0, c.jsxs)("button", {
60
- "data-id": e,
61
- onClick: () => l(e),
62
- className: `flex items-center justify-center gap-1.5 focus:outline-none border-none outline-none z-10 transition-colors select-none h-full ${{
63
- underline: `pb-2 pt-1 px-3 text-xs font-semibold cursor-pointer border-b-2 ${u ? "border-vsc-accent text-vsc-text" : "border-transparent text-vsc-muted hover:text-vsc-text"}`,
64
- "sliding-underline": `py-2 px-4 text-xs font-semibold cursor-pointer transition-colors duration-200 ${u ? "text-vsc-text font-bold" : "text-vsc-muted hover:text-vsc-text"}`,
65
- pill: `px-3.5 py-1.5 text-xs font-bold rounded-full cursor-pointer scale-100 ${u ? "bg-vsc-accent text-vsc-button-text shadow-sm" : "bg-vsc-hover/40 text-vsc-muted hover:bg-vsc-hover"}`,
66
- vscode: `px-4 text-xs font-medium border-r border-vsc-border cursor-pointer ${u ? "bg-vsc-bg text-vsc-text border-t-2 border-t-vsc-accent -mt-[1px]" : "bg-vsc-sidebar text-vsc-muted hover:bg-vsc-hover/50"}`,
67
- ghost: `px-3 py-1.5 text-xs font-semibold rounded cursor-pointer ${u ? "bg-vsc-hover text-vsc-text font-bold" : "text-vsc-muted hover:bg-vsc-hover/30"}`
68
- }[r]}`,
69
- children: [
70
- i && /* @__PURE__ */ (0, c.jsx)("span", {
71
- className: "shrink-0 opacity-80",
72
- children: i
73
- }),
74
- /* @__PURE__ */ (0, c.jsx)("span", {
75
- className: "truncate",
76
- children: n
77
- }),
78
- a && /* @__PURE__ */ (0, c.jsx)("span", {
79
- className: "shrink-0 opacity-70",
80
- children: a
81
- })
82
- ]
83
- });
84
- }
85
- //#endregion
86
- export { s as i, f as n, d as r, p as t };