@pantoia/ui 0.14.1 → 0.15.1

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": "@pantoia/ui",
3
- "version": "0.14.1",
3
+ "version": "0.15.1",
4
4
  "description": "Pantoia Design System",
5
5
  "homepage": "https://design.pantoia.com",
6
6
  "repository": {
package/palette.css CHANGED
@@ -3,9 +3,9 @@
3
3
  --pds-gray-400:#b0b8c1; --pds-gray-500:#8b95a1; --pds-gray-600:#6b7684; --pds-gray-700:#4e5968;
4
4
  --pds-gray-800:#333d4b; --pds-gray-900:#191f28;
5
5
 
6
- --pds-brand-50:#e4ebf6; --pds-brand-100:#c7daf7; --pds-brand-200:#9fc1f8; --pds-brand-300:#78a7f9;
7
- --pds-brand-400:#5c90f9; --pds-brand-500:#4c7bf7; --pds-brand-600:#4569ed; --pds-brand-700:#3e5eda;
8
- --pds-brand-800:#3653c3; --pds-brand-900:#2e48aa;
6
+ --pds-brand-50:#e5f9f3; --pds-brand-100:#b7e5d8; --pds-brand-200:#84cfbc; --pds-brand-300:#6eb2a1;
7
+ --pds-brand-400:#43a18d; --pds-brand-500:#01937c; --pds-brand-600:#008671; --pds-brand-700:#017b67;
8
+ --pds-brand-800:#036e5d; --pds-brand-900:#036252;
9
9
 
10
10
  --pds-blue-50:#e8f3ff; --pds-blue-100:#c9e2ff; --pds-blue-200:#90c2ff; --pds-blue-300:#64a8ff;
11
11
  --pds-blue-400:#4593fc; --pds-blue-500:#3182f6; --pds-blue-600:#2272eb; --pds-blue-700:#1b64da;
@@ -29,9 +29,9 @@
29
29
  --pds-gray-400:#62626d; --pds-gray-500:#7e7e87; --pds-gray-600:#9e9ea4; --pds-gray-700:#c3c3c6;
30
30
  --pds-gray-800:#e4e4e5; --pds-gray-900:#ffffff;
31
31
 
32
- --pds-brand-50:#222f4e; --pds-brand-100:#23396f; --pds-brand-200:#29448e; --pds-brand-300:#3051af;
33
- --pds-brand-400:#3d67d5; --pds-brand-500:#4c7bf7; --pds-brand-600:#6290ff; --pds-brand-700:#86aaff;
34
- --pds-brand-800:#b1c8ff; --pds-brand-900:#d9e5ff;
32
+ --pds-brand-50:#1d3630; --pds-brand-100:#13453b; --pds-brand-200:#065547; --pds-brand-300:#036555;
33
+ --pds-brand-400:#037d69; --pds-brand-500:#01937c; --pds-brand-600:#41a48f; --pds-brand-700:#6abba8;
34
+ --pds-brand-800:#9bd1c3; --pds-brand-900:#d7eae4;
35
35
 
36
36
  --pds-blue-50:#202c4d; --pds-blue-100:#23386a; --pds-blue-200:#25478c; --pds-blue-300:#265ab3;
37
37
  --pds-blue-400:#2970d9; --pds-blue-500:#3485fa; --pds-blue-600:#449bff; --pds-blue-700:#61b0ff;
package/registry.json CHANGED
@@ -585,6 +585,10 @@
585
585
  {
586
586
  "path": "segmented-control/index.tsx",
587
587
  "type": "registry:ui"
588
+ },
589
+ {
590
+ "path": "segmented-control/index.module.css",
591
+ "type": "registry:ui"
588
592
  }
589
593
  ],
590
594
  "dependencies": [
@@ -0,0 +1,19 @@
1
+ .thumb {
2
+ display: none;
3
+ position: absolute;
4
+ inset-block: 4px;
5
+ left: 0;
6
+ width: var(--segmented-control-width);
7
+ translate: var(--segmented-control-left) 0;
8
+ border-radius: var(--radius-r6);
9
+ background: var(--color-bg-neutral-thumb);
10
+ box-shadow: var(--shadow-s1);
11
+ transition: translate var(--dur-state) var(--ease-state),
12
+ width var(--dur-state) var(--ease-state);
13
+ }
14
+
15
+ [data-thumb] > .thumb { display: block; }
16
+
17
+ @media (prefers-reduced-motion: reduce) {
18
+ .thumb { transition: none; }
19
+ }
@@ -2,21 +2,24 @@
2
2
 
3
3
  import { Radio } from '@base-ui/react/radio'
4
4
  import { RadioGroup } from '@base-ui/react/radio-group'
5
- import { createContext, use, type ComponentProps } from 'react'
5
+ import { createContext, use, useLayoutEffect, useState, type ComponentProps } from 'react'
6
6
 
7
7
  import { tv } from '../tv'
8
8
 
9
+ import s from './index.module.css'
10
+
9
11
  type Size = 'sm' | 'md' | 'lg'
10
12
 
11
13
  const Context = createContext<{ size: Size, fill: boolean }>({ size: 'md', fill: false })
12
14
 
13
15
  export const segmentedControl = tv({
14
16
  slots: {
15
- root: 'bg-bg-neutral-selected rounded-r10 inline-flex shrink-0 gap-0.5 p-1',
16
- item: 'text-label text-fg-muted rounded-r6 inline-flex h-full items-center justify-center gap-tight whitespace-nowrap'
17
+ root: 'group/segmented-control bg-bg-neutral-selected rounded-r10 relative inline-flex shrink-0 gap-0.5 p-1',
18
+ item: 'text-label text-fg-muted rounded-r6 relative inline-flex h-full items-center justify-center gap-tight whitespace-nowrap'
17
19
  + ' transition-state press-shrink cursor-pointer'
18
20
  + ' not-data-disabled:hover:text-fg'
19
21
  + ' checked:bg-bg-neutral-thumb checked:text-fg checked:shadow-s1'
22
+ + ' group-data-thumb/segmented-control:checked:bg-transparent group-data-thumb/segmented-control:checked:shadow-none'
20
23
  + ' focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-line-focus-ring'
21
24
  + ' data-disabled:text-fg-disabled data-disabled:cursor-not-allowed',
22
25
  },
@@ -42,12 +45,45 @@ export type SegmentedControlProps<Value> = Omit<ComponentProps<typeof RadioGroup
42
45
  className?: string
43
46
  }
44
47
 
45
- export function SegmentedControl<Value>({ size = 'md', fill = false, className, ...rest }: SegmentedControlProps<Value>) {
48
+ export function SegmentedControl<Value>({ size = 'md', fill = false, className, children, ref, ...rest }: SegmentedControlProps<Value>) {
46
49
  const ui = segmentedControl({ size, fill })
50
+ const [root, setRoot] = useState<HTMLDivElement | null>(null)
51
+
52
+ useLayoutEffect(() => {
53
+ if (!root) return
54
+ const place = () => {
55
+ const on = root.querySelector<HTMLElement>('[role="radio"][data-checked]')
56
+ if (!on) {
57
+ root.removeAttribute('data-thumb')
58
+ return
59
+ }
60
+ root.style.setProperty('--segmented-control-left', `${on.offsetLeft}px`)
61
+ root.style.setProperty('--segmented-control-width', `${on.offsetWidth}px`)
62
+ root.setAttribute('data-thumb', '')
63
+ }
64
+ place()
65
+ const resize = new ResizeObserver(place)
66
+ const change = new MutationObserver(place)
67
+ resize.observe(root)
68
+ change.observe(root, { subtree: true, attributeFilter: ['data-checked'] })
69
+ return () => {
70
+ resize.disconnect()
71
+ change.disconnect()
72
+ }
73
+ }, [root])
74
+
75
+ const attach = (el: HTMLDivElement | null) => {
76
+ setRoot(el)
77
+ if (typeof ref === 'function') ref(el)
78
+ else if (ref) ref.current = el
79
+ }
47
80
 
48
81
  return (
49
82
  <Context value={{ size, fill }}>
50
- <RadioGroup {...rest} className={ui.root({ className })} />
83
+ <RadioGroup {...rest} ref={attach} className={ui.root({ className })}>
84
+ <span aria-hidden className={s.thumb} />
85
+ {children}
86
+ </RadioGroup>
51
87
  </Context>
52
88
  )
53
89
  }
package/tabs/index.tsx CHANGED
@@ -34,7 +34,7 @@ export function TabsList({ className, children, ...rest }: TabsListProps) {
34
34
  const tabsTrigger = tv({
35
35
  base: 'text-label text-fg-muted inline-flex h-9 items-center gap-tight px-3 transition-state'
36
36
  + ' [&_svg]:size-4'
37
- + ' press-shrink not-data-disabled:cursor-pointer not-data-disabled:hover:text-fg'
37
+ + ' not-data-disabled:cursor-pointer not-data-disabled:hover:text-fg'
38
38
  + ' data-disabled:text-fg-disabled data-disabled:cursor-not-allowed'
39
39
  + ' aria-selected:text-fg'
40
40
  + ' focus-visible:outline-2 focus-visible:-outline-offset-2'