@toolpath/ui 0.1.0
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/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/index.d.ts +1174 -0
- package/dist/index.js +5934 -0
- package/package.json +68 -0
- package/src/attribution/action.tsx +13 -0
- package/src/attribution/at.tsx +28 -0
- package/src/attribution/attribution.tsx +16 -0
- package/src/attribution/by.tsx +21 -0
- package/src/attribution/format-time.ts +48 -0
- package/src/attribution/index.ts +20 -0
- package/src/badge/badge.tsx +38 -0
- package/src/badge/index.ts +1 -0
- package/src/breadcrumbs/breadcrumbs-context.tsx +26 -0
- package/src/breadcrumbs/breadcrumbs.tsx +24 -0
- package/src/breadcrumbs/index.ts +12 -0
- package/src/breadcrumbs/item.tsx +85 -0
- package/src/button/animated-ellipsis.tsx +44 -0
- package/src/button/button.tsx +168 -0
- package/src/button/index.ts +1 -0
- package/src/callout/callout.tsx +202 -0
- package/src/callout/index.ts +2 -0
- package/src/callout/use-dismissed-callouts.ts +78 -0
- package/src/card/card.tsx +19 -0
- package/src/card/index.ts +2 -0
- package/src/checkbox/checkbox.tsx +76 -0
- package/src/checkbox/index.ts +1 -0
- package/src/combobox/button.tsx +99 -0
- package/src/combobox/combobox-context.tsx +42 -0
- package/src/combobox/combobox.tsx +60 -0
- package/src/combobox/empty.tsx +21 -0
- package/src/combobox/group-label.tsx +20 -0
- package/src/combobox/group.tsx +11 -0
- package/src/combobox/icon.tsx +11 -0
- package/src/combobox/index.tsx +64 -0
- package/src/combobox/input.tsx +77 -0
- package/src/combobox/item-indicator.tsx +16 -0
- package/src/combobox/item.tsx +58 -0
- package/src/combobox/list.tsx +19 -0
- package/src/combobox/popover.tsx +62 -0
- package/src/combobox/trigger.tsx +33 -0
- package/src/combobox/value.tsx +12 -0
- package/src/common/index.ts +1 -0
- package/src/common/svg.tsx +17 -0
- package/src/dialog/actions.tsx +17 -0
- package/src/dialog/confirm-button.tsx +25 -0
- package/src/dialog/dialog-context.tsx +21 -0
- package/src/dialog/dialog.tsx +37 -0
- package/src/dialog/dismiss-button.tsx +29 -0
- package/src/dialog/index.ts +37 -0
- package/src/dialog/message.tsx +20 -0
- package/src/dialog/provider.tsx +102 -0
- package/src/dialog/title.tsx +20 -0
- package/src/editable-cell/editable-cell.tsx +307 -0
- package/src/editable-cell/index.ts +7 -0
- package/src/editable-cell/use-editable-grid.tsx +296 -0
- package/src/field/field.tsx +22 -0
- package/src/field/index.ts +2 -0
- package/src/helpers/cn.ts +6 -0
- package/src/helpers/index.ts +1 -0
- package/src/icon-button/icon-button.tsx +126 -0
- package/src/icon-button/index.ts +2 -0
- package/src/index.ts +32 -0
- package/src/input/index.ts +1 -0
- package/src/input/input.tsx +276 -0
- package/src/link/index.ts +4 -0
- package/src/link/link-context.tsx +33 -0
- package/src/link/link.tsx +24 -0
- package/src/menu/divider.tsx +17 -0
- package/src/menu/index.ts +40 -0
- package/src/menu/item.tsx +149 -0
- package/src/menu/menu-context.tsx +24 -0
- package/src/menu/menu.tsx +14 -0
- package/src/menu/popover.tsx +69 -0
- package/src/menu/submenu-trigger.tsx +48 -0
- package/src/menu/submenu.tsx +20 -0
- package/src/menu/trigger.tsx +43 -0
- package/src/notification/index.ts +16 -0
- package/src/notification/list.tsx +123 -0
- package/src/notification/provider.tsx +75 -0
- package/src/page-header/index.ts +2 -0
- package/src/page-header/page-header.tsx +14 -0
- package/src/pagination/index.ts +1 -0
- package/src/pagination/pagination.tsx +136 -0
- package/src/panels/collapsed.tsx +32 -0
- package/src/panels/group.tsx +27 -0
- package/src/panels/index.ts +57 -0
- package/src/panels/layout-context.ts +5 -0
- package/src/panels/orientation-context.tsx +5 -0
- package/src/panels/panel.tsx +95 -0
- package/src/panels/separator.tsx +44 -0
- package/src/pin/index.ts +1 -0
- package/src/pin/pin.tsx +99 -0
- package/src/progress-bar/index.ts +1 -0
- package/src/progress-bar/progress-bar.tsx +48 -0
- package/src/quantity-input/index.ts +1 -0
- package/src/quantity-input/quantity-input.tsx +158 -0
- package/src/scroll-area/index.ts +1 -0
- package/src/scroll-area/scroll-area.tsx +82 -0
- package/src/slider/index.ts +1 -0
- package/src/slider/slider.tsx +75 -0
- package/src/star/index.ts +1 -0
- package/src/star/star.tsx +99 -0
- package/src/switch/index.ts +1 -0
- package/src/switch/switch.tsx +33 -0
- package/src/table/body.tsx +15 -0
- package/src/table/cell-context.tsx +48 -0
- package/src/table/cell.tsx +83 -0
- package/src/table/group-empty-state.tsx +28 -0
- package/src/table/group-header.tsx +62 -0
- package/src/table/header-cell.tsx +158 -0
- package/src/table/header-row.tsx +28 -0
- package/src/table/index.ts +56 -0
- package/src/table/is-group-empty-state.ts +17 -0
- package/src/table/is-group-header.ts +5 -0
- package/src/table/row-context.tsx +61 -0
- package/src/table/row-disabled-context.tsx +7 -0
- package/src/table/row.tsx +98 -0
- package/src/table/table-context.tsx +127 -0
- package/src/table/table-empty.tsx +57 -0
- package/src/table/table.tsx +547 -0
- package/src/table/tree-icon.tsx +147 -0
- package/src/table/use-checkbox.ts +200 -0
- package/src/table/use-column-layout.ts +117 -0
- package/src/table/use-groups.ts +134 -0
- package/src/table/use-keyboard-navigation.ts +274 -0
- package/src/table/use-sort-functions.ts +90 -0
- package/src/tabs/index.ts +20 -0
- package/src/tabs/list.tsx +29 -0
- package/src/tabs/tab.tsx +141 -0
- package/src/tabs/tabs-context.tsx +38 -0
- package/src/tabs/tabs.tsx +27 -0
- package/src/textarea/index.ts +1 -0
- package/src/textarea/textarea.tsx +88 -0
- package/src/toast/index.ts +19 -0
- package/src/toast/list.tsx +133 -0
- package/src/toast/provider.tsx +36 -0
- package/src/toggle/index.ts +17 -0
- package/src/toggle/indicator.tsx +27 -0
- package/src/toggle/item.tsx +54 -0
- package/src/toggle/toggle-context.tsx +68 -0
- package/src/toggle/toggle.tsx +124 -0
- package/src/toggle/use-indicator.tsx +69 -0
- package/src/tooltip/index.ts +1 -0
- package/src/tooltip/tooltip.tsx +122 -0
- package/theme.css +70 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, { FC } from 'react'
|
|
2
|
+
import { cn } from '../helpers'
|
|
3
|
+
import { CaretRightIcon } from '@phosphor-icons/react'
|
|
4
|
+
|
|
5
|
+
export type PaginationMetadata = {
|
|
6
|
+
totalPages: number
|
|
7
|
+
perPage: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type PaginationProps = {
|
|
11
|
+
pagination: PaginationMetadata
|
|
12
|
+
page: number
|
|
13
|
+
onClick: (page: number) => void
|
|
14
|
+
className?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Pagination: FC<PaginationProps> = ({ pagination, page, onClick, className }) => {
|
|
18
|
+
const isDisabled = pagination.totalPages <= 1
|
|
19
|
+
|
|
20
|
+
const paginationLink = (linkedPage: number, ellipses: boolean = false) => (
|
|
21
|
+
<li
|
|
22
|
+
key={ellipses ? `ellipsis-${linkedPage}` : linkedPage}
|
|
23
|
+
className="inline-block text-xs tracking-tight text-gray-300 dark:text-zinc-400"
|
|
24
|
+
>
|
|
25
|
+
<button
|
|
26
|
+
disabled={page === linkedPage || isDisabled}
|
|
27
|
+
className={cn(
|
|
28
|
+
'select-none rounded-sm',
|
|
29
|
+
'hover:bg-gray-50 hover:text-gray dark:hover:bg-zinc-800 dark:hover:text-zinc-100',
|
|
30
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-info/75',
|
|
31
|
+
'font-bold transition-colors duration-200',
|
|
32
|
+
{
|
|
33
|
+
'bg-gray-300 text-white hover:bg-gray-300 hover:text-white dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800 dark:hover:text-zinc-50':
|
|
34
|
+
page === linkedPage && !isDisabled,
|
|
35
|
+
'hidden sm:block': page !== linkedPage,
|
|
36
|
+
'bg-gray-100 text-white hover:bg-gray-100 hover:text-white dark:bg-zinc-900 dark:text-zinc-500 dark:hover:bg-zinc-900 dark:hover:text-zinc-500':
|
|
37
|
+
isDisabled,
|
|
38
|
+
},
|
|
39
|
+
)}
|
|
40
|
+
onClick={() => onClick(linkedPage)}
|
|
41
|
+
>
|
|
42
|
+
<div className="min-w-4 px-1 py-px">{ellipses ? '…' : linkedPage}</div>
|
|
43
|
+
</button>
|
|
44
|
+
</li>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className={cn('flex whitespace-nowrap', className)}>
|
|
49
|
+
<button
|
|
50
|
+
disabled={page === 1}
|
|
51
|
+
className={cn(
|
|
52
|
+
'mt-px flex items-center rounded-sm font-semibold',
|
|
53
|
+
'text-gray-300 hover:bg-gray-50',
|
|
54
|
+
'dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100',
|
|
55
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-info/75',
|
|
56
|
+
{
|
|
57
|
+
'cursor-not-allowed text-gray-100 hover:bg-transparent dark:text-zinc-600 dark:hover:bg-transparent':
|
|
58
|
+
page === 1,
|
|
59
|
+
},
|
|
60
|
+
)}
|
|
61
|
+
onClick={() => onClick(page - 1)}
|
|
62
|
+
>
|
|
63
|
+
<CaretRightIcon className="size-5 rotate-180" />
|
|
64
|
+
<span className="sr-only">Previous</span>
|
|
65
|
+
</button>
|
|
66
|
+
<nav className="text-sm text-gray-400 dark:text-zinc-300">
|
|
67
|
+
<ul>
|
|
68
|
+
{paginationLink(1)}
|
|
69
|
+
{pagination.totalPages >= 2 &&
|
|
70
|
+
(pagination.totalPages <= 5 || page < 4) &&
|
|
71
|
+
paginationLink(2)}
|
|
72
|
+
{pagination.totalPages >= 3 &&
|
|
73
|
+
(pagination.totalPages <= 5 || page < 4) &&
|
|
74
|
+
paginationLink(3)}
|
|
75
|
+
{pagination.totalPages === 5 && paginationLink(4)}
|
|
76
|
+
{page > 3 &&
|
|
77
|
+
pagination.totalPages > 5 &&
|
|
78
|
+
paginationLink(
|
|
79
|
+
page >= pagination.totalPages - 2 ? pagination.totalPages - 3 : page - 2,
|
|
80
|
+
true,
|
|
81
|
+
)}
|
|
82
|
+
{pagination.totalPages > 6 &&
|
|
83
|
+
page >= 4 &&
|
|
84
|
+
page < pagination.totalPages - 2 &&
|
|
85
|
+
paginationLink(page - 1)}
|
|
86
|
+
{pagination.totalPages > 6 &&
|
|
87
|
+
page >= 4 &&
|
|
88
|
+
page < pagination.totalPages - 2 &&
|
|
89
|
+
paginationLink(page)}
|
|
90
|
+
{pagination.totalPages > 6 &&
|
|
91
|
+
page >= 4 &&
|
|
92
|
+
page < pagination.totalPages - 2 &&
|
|
93
|
+
paginationLink(page + 1)}
|
|
94
|
+
|
|
95
|
+
{pagination.totalPages > 5 &&
|
|
96
|
+
page < pagination.totalPages - 2 &&
|
|
97
|
+
paginationLink(
|
|
98
|
+
page < 3
|
|
99
|
+
? 4
|
|
100
|
+
: page === 3
|
|
101
|
+
? page + 1
|
|
102
|
+
: page <= pagination.totalPages - 3
|
|
103
|
+
? page + 2
|
|
104
|
+
: pagination.totalPages - 4,
|
|
105
|
+
true,
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
{pagination.totalPages > 5 &&
|
|
109
|
+
page >= pagination.totalPages - 2 &&
|
|
110
|
+
paginationLink(pagination.totalPages - 2)}
|
|
111
|
+
{pagination.totalPages > 5 &&
|
|
112
|
+
page >= pagination.totalPages - 2 &&
|
|
113
|
+
paginationLink(pagination.totalPages - 1)}
|
|
114
|
+
{pagination.totalPages > 3 && paginationLink(pagination.totalPages)}
|
|
115
|
+
</ul>
|
|
116
|
+
</nav>
|
|
117
|
+
<button
|
|
118
|
+
disabled={page >= pagination.totalPages}
|
|
119
|
+
className={cn(
|
|
120
|
+
'mt-px flex items-center rounded-sm font-semibold',
|
|
121
|
+
'text-gray-300 hover:bg-gray-50',
|
|
122
|
+
'dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100',
|
|
123
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-info/75',
|
|
124
|
+
{
|
|
125
|
+
'cursor-not-allowed text-gray-100 hover:bg-transparent dark:text-zinc-600 dark:hover:bg-transparent':
|
|
126
|
+
page >= pagination.totalPages,
|
|
127
|
+
},
|
|
128
|
+
)}
|
|
129
|
+
onClick={() => onClick(page + 1)}
|
|
130
|
+
>
|
|
131
|
+
<span className="sr-only">Next</span>
|
|
132
|
+
<CaretRightIcon className="size-5" />
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, { FC, KeyboardEvent } from 'react'
|
|
2
|
+
import { DotsSixVerticalIcon } from '@phosphor-icons/react'
|
|
3
|
+
import { useOrientation } from './orientation-context'
|
|
4
|
+
|
|
5
|
+
export interface CollapsedProps {
|
|
6
|
+
onExpand: () => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Collapsed: FC<CollapsedProps> = ({ onExpand }) => {
|
|
10
|
+
const orientation = useOrientation()
|
|
11
|
+
const isVertical = orientation === 'vertical'
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<button
|
|
15
|
+
type="button"
|
|
16
|
+
data-handle
|
|
17
|
+
onMouseDown={onExpand}
|
|
18
|
+
onKeyDown={(e: KeyboardEvent) => {
|
|
19
|
+
if (e.key === ' ') {
|
|
20
|
+
e.preventDefault()
|
|
21
|
+
onExpand()
|
|
22
|
+
}
|
|
23
|
+
}}
|
|
24
|
+
className={`flex h-full relative w-full items-center justify-center group outline-none focus-visible:ring-2 ring-info/75 ring-inset ${isVertical ? 'cursor-row-resize' : 'cursor-col-resize'}`}
|
|
25
|
+
>
|
|
26
|
+
<DotsSixVerticalIcon
|
|
27
|
+
data-handle-icon
|
|
28
|
+
className={`size-4 text-gray-200 dark:text-zinc-500 group-hover:text-gray-300 dark:group-hover:text-zinc-400 ${isVertical ? 'rotate-90' : ''}`}
|
|
29
|
+
/>
|
|
30
|
+
</button>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
|
+
import { Group as BaseGroup, type GroupProps, useGroupRef } from 'react-resizable-panels'
|
|
3
|
+
import { LayoutContext } from './layout-context'
|
|
4
|
+
import { OrientationContext } from './orientation-context'
|
|
5
|
+
|
|
6
|
+
export const Group = ({ orientation = 'horizontal', onLayoutChanged, ...props }: GroupProps) => {
|
|
7
|
+
const groupRef = useGroupRef()
|
|
8
|
+
|
|
9
|
+
const notifyLayoutChanged = useCallback(() => {
|
|
10
|
+
if (onLayoutChanged && groupRef.current) {
|
|
11
|
+
onLayoutChanged(groupRef.current.getLayout())
|
|
12
|
+
}
|
|
13
|
+
}, [onLayoutChanged, groupRef])
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<OrientationContext.Provider value={orientation}>
|
|
17
|
+
<LayoutContext.Provider value={notifyLayoutChanged}>
|
|
18
|
+
<BaseGroup
|
|
19
|
+
orientation={orientation}
|
|
20
|
+
groupRef={groupRef}
|
|
21
|
+
onLayoutChanged={onLayoutChanged}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
</LayoutContext.Provider>
|
|
25
|
+
</OrientationContext.Provider>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isCoarsePointer as _isCoarsePointer,
|
|
3
|
+
useDefaultLayout as _useDefaultLayout,
|
|
4
|
+
useGroupCallbackRef as _useGroupCallbackRef,
|
|
5
|
+
useGroupRef as _useGroupRef,
|
|
6
|
+
usePanelCallbackRef as _usePanelCallbackRef,
|
|
7
|
+
usePanelRef as _usePanelRef,
|
|
8
|
+
} from 'react-resizable-panels'
|
|
9
|
+
import type {
|
|
10
|
+
GroupImperativeHandle as _GroupImperativeHandle,
|
|
11
|
+
GroupProps as _GroupProps,
|
|
12
|
+
Layout as _Layout,
|
|
13
|
+
LayoutStorage as _LayoutStorage,
|
|
14
|
+
OnGroupLayoutChange as _OnGroupLayoutChange,
|
|
15
|
+
OnPanelResize as _OnPanelResize,
|
|
16
|
+
Orientation as _Orientation,
|
|
17
|
+
PanelImperativeHandle as _PanelImperativeHandle,
|
|
18
|
+
PanelProps as _PanelProps,
|
|
19
|
+
PanelSize as _PanelSize,
|
|
20
|
+
SeparatorProps as _SeparatorProps,
|
|
21
|
+
SizeUnit as _SizeUnit,
|
|
22
|
+
} from 'react-resizable-panels'
|
|
23
|
+
import { Collapsed as _Collapsed } from './collapsed'
|
|
24
|
+
import type { CollapsedProps as _CollapsedProps } from './collapsed'
|
|
25
|
+
import { useOrientation as _useOrientation } from './orientation-context'
|
|
26
|
+
import { Group as _Group } from './group'
|
|
27
|
+
import { Panel as _Panel } from './panel'
|
|
28
|
+
import { Separator as _Separator } from './separator'
|
|
29
|
+
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
31
|
+
export namespace Panels {
|
|
32
|
+
export const Group = _Group
|
|
33
|
+
export const Panel = _Panel
|
|
34
|
+
export const Separator = _Separator
|
|
35
|
+
export const Collapsed = _Collapsed
|
|
36
|
+
export const isCoarsePointer = _isCoarsePointer
|
|
37
|
+
export const useDefaultLayout = _useDefaultLayout
|
|
38
|
+
export const useGroupCallbackRef = _useGroupCallbackRef
|
|
39
|
+
export const useGroupRef = _useGroupRef
|
|
40
|
+
export const usePanelCallbackRef = _usePanelCallbackRef
|
|
41
|
+
export const useOrientation = _useOrientation
|
|
42
|
+
export const usePanelRef = _usePanelRef
|
|
43
|
+
|
|
44
|
+
export type GroupImperativeHandle = _GroupImperativeHandle
|
|
45
|
+
export type GroupProps = _GroupProps
|
|
46
|
+
export type Layout = _Layout
|
|
47
|
+
export type LayoutStorage = _LayoutStorage
|
|
48
|
+
export type OnGroupLayoutChange = _OnGroupLayoutChange
|
|
49
|
+
export type OnPanelResize = _OnPanelResize
|
|
50
|
+
export type Orientation = _Orientation
|
|
51
|
+
export type PanelImperativeHandle = _PanelImperativeHandle
|
|
52
|
+
export type PanelProps = _PanelProps
|
|
53
|
+
export type PanelSize = _PanelSize
|
|
54
|
+
export type SeparatorProps = _SeparatorProps
|
|
55
|
+
export type SizeUnit = _SizeUnit
|
|
56
|
+
export type CollapsedProps = _CollapsedProps
|
|
57
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React, { ReactNode, useCallback, useRef, useState } from 'react'
|
|
2
|
+
import { flushSync } from 'react-dom'
|
|
3
|
+
import {
|
|
4
|
+
Panel as BasePanel,
|
|
5
|
+
type PanelProps as BasePanelProps,
|
|
6
|
+
type PanelSize,
|
|
7
|
+
usePanelRef,
|
|
8
|
+
} from 'react-resizable-panels'
|
|
9
|
+
import { Collapsed } from './collapsed'
|
|
10
|
+
import { useLayoutChanged } from './layout-context'
|
|
11
|
+
|
|
12
|
+
type PanelProps = BasePanelProps & {
|
|
13
|
+
children?: ReactNode
|
|
14
|
+
onExpand?: () => void
|
|
15
|
+
onCollapse?: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const DEFAULT_COLLAPSED_SIZE = 20
|
|
19
|
+
|
|
20
|
+
export const Panel = ({
|
|
21
|
+
collapsible,
|
|
22
|
+
collapsedSize = collapsible ? DEFAULT_COLLAPSED_SIZE : undefined,
|
|
23
|
+
panelRef: panelRefProp,
|
|
24
|
+
onResize: onResizeProp,
|
|
25
|
+
onExpand: onExpandProp,
|
|
26
|
+
onCollapse: onCollapseProp,
|
|
27
|
+
defaultSize,
|
|
28
|
+
children,
|
|
29
|
+
...props
|
|
30
|
+
}: PanelProps) => {
|
|
31
|
+
const internalRef = usePanelRef()
|
|
32
|
+
const panelRef = panelRefProp ?? (collapsible ? internalRef : undefined)
|
|
33
|
+
|
|
34
|
+
const collapsedPx = typeof collapsedSize === 'number' ? collapsedSize : 0
|
|
35
|
+
const [width, setWidth] = useState(
|
|
36
|
+
typeof defaultSize === 'number' ? defaultSize : collapsedPx + 1,
|
|
37
|
+
)
|
|
38
|
+
const wasCollapsedRef = useRef(false)
|
|
39
|
+
|
|
40
|
+
const notifyLayoutChanged = useLayoutChanged()
|
|
41
|
+
|
|
42
|
+
const handleResize = useCallback(
|
|
43
|
+
(size: PanelSize, id: string | number | undefined, prevSize: PanelSize | undefined) => {
|
|
44
|
+
const isCollapsed = Boolean(collapsible && size.inPixels <= collapsedPx)
|
|
45
|
+
const wasCollapsed = wasCollapsedRef.current
|
|
46
|
+
if (isCollapsed !== wasCollapsed) {
|
|
47
|
+
wasCollapsedRef.current = isCollapsed
|
|
48
|
+
flushSync(() => setWidth(size.inPixels))
|
|
49
|
+
if (isCollapsed) {
|
|
50
|
+
onCollapseProp?.()
|
|
51
|
+
} else {
|
|
52
|
+
onExpandProp?.()
|
|
53
|
+
}
|
|
54
|
+
if (props.id) {
|
|
55
|
+
notifyLayoutChanged?.()
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
setWidth(size.inPixels)
|
|
59
|
+
}
|
|
60
|
+
onResizeProp?.(size, id, prevSize)
|
|
61
|
+
},
|
|
62
|
+
[
|
|
63
|
+
collapsible,
|
|
64
|
+
collapsedPx,
|
|
65
|
+
onResizeProp,
|
|
66
|
+
onExpandProp,
|
|
67
|
+
onCollapseProp,
|
|
68
|
+
props.id,
|
|
69
|
+
notifyLayoutChanged,
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const handleExpand = useCallback(() => {
|
|
74
|
+
if (panelRef && 'current' in panelRef) {
|
|
75
|
+
panelRef.current?.expand()
|
|
76
|
+
}
|
|
77
|
+
onExpandProp?.()
|
|
78
|
+
if (props.id) {
|
|
79
|
+
notifyLayoutChanged?.()
|
|
80
|
+
}
|
|
81
|
+
}, [panelRef, onExpandProp, props.id, notifyLayoutChanged])
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<BasePanel
|
|
85
|
+
collapsible={collapsible}
|
|
86
|
+
collapsedSize={collapsedSize}
|
|
87
|
+
panelRef={panelRef}
|
|
88
|
+
onResize={collapsible ? handleResize : onResizeProp}
|
|
89
|
+
defaultSize={defaultSize}
|
|
90
|
+
{...props}
|
|
91
|
+
>
|
|
92
|
+
{collapsible && width <= collapsedPx ? <Collapsed onExpand={handleExpand} /> : children}
|
|
93
|
+
</BasePanel>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Separator as BaseSeparator,
|
|
4
|
+
type SeparatorProps as BaseSeparatorProps,
|
|
5
|
+
} from 'react-resizable-panels'
|
|
6
|
+
import { cn } from '../helpers'
|
|
7
|
+
import { useOrientation } from './orientation-context'
|
|
8
|
+
|
|
9
|
+
type SeparatorProps = Omit<BaseSeparatorProps, 'elementRef'> & {
|
|
10
|
+
elementRef?: BaseSeparatorProps['elementRef']
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Separator = ({ elementRef, className, ...props }: SeparatorProps) => {
|
|
14
|
+
const orientation = useOrientation()
|
|
15
|
+
|
|
16
|
+
const ref = useCallback(
|
|
17
|
+
(el: HTMLDivElement | null) => {
|
|
18
|
+
if (el) {
|
|
19
|
+
el.tabIndex = -1
|
|
20
|
+
}
|
|
21
|
+
if (typeof elementRef === 'function') {
|
|
22
|
+
elementRef(el)
|
|
23
|
+
} else if (elementRef) {
|
|
24
|
+
elementRef.current = el
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
[elementRef],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<BaseSeparator
|
|
32
|
+
elementRef={ref}
|
|
33
|
+
className={cn(
|
|
34
|
+
'border-gray-100 dark:border-zinc-800 outline-none',
|
|
35
|
+
{
|
|
36
|
+
'border-r': orientation === 'horizontal',
|
|
37
|
+
'border-t': orientation === 'vertical',
|
|
38
|
+
},
|
|
39
|
+
className,
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
package/src/pin/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './pin'
|
package/src/pin/pin.tsx
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
|
+
import { cn } from '../helpers'
|
|
3
|
+
import { PushPinIcon } from '@phosphor-icons/react'
|
|
4
|
+
|
|
5
|
+
export interface PinChangeEvent {
|
|
6
|
+
shiftKey: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type PinProps = {
|
|
10
|
+
checked: boolean
|
|
11
|
+
onChange: (checked: boolean, event: PinChangeEvent) => void
|
|
12
|
+
name: string
|
|
13
|
+
className?: string
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
size?: 'sm' | 'md' | 'lg'
|
|
16
|
+
tabIndex?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const Pin = ({
|
|
20
|
+
checked,
|
|
21
|
+
onChange,
|
|
22
|
+
name,
|
|
23
|
+
className,
|
|
24
|
+
disabled = false,
|
|
25
|
+
size = 'md',
|
|
26
|
+
tabIndex = 0,
|
|
27
|
+
}: PinProps) => {
|
|
28
|
+
const shiftKeyRef = useRef(false)
|
|
29
|
+
|
|
30
|
+
const handleClick = (e: React.MouseEvent) => {
|
|
31
|
+
e.stopPropagation()
|
|
32
|
+
if (disabled) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
shiftKeyRef.current = e.shiftKey
|
|
36
|
+
onChange(!checked, { shiftKey: shiftKeyRef.current })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
40
|
+
if (disabled) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if (e.key === ' ' || e.key === 'Enter') {
|
|
44
|
+
e.preventDefault()
|
|
45
|
+
shiftKeyRef.current = e.shiftKey
|
|
46
|
+
onChange(!checked, { shiftKey: shiftKeyRef.current })
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const sizeClasses = {
|
|
51
|
+
sm: 'size-3',
|
|
52
|
+
md: 'size-4',
|
|
53
|
+
lg: 'size-5',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
role="checkbox"
|
|
60
|
+
aria-checked={checked}
|
|
61
|
+
name={name}
|
|
62
|
+
disabled={disabled}
|
|
63
|
+
tabIndex={tabIndex}
|
|
64
|
+
onClick={handleClick}
|
|
65
|
+
onKeyDown={handleKeyDown}
|
|
66
|
+
className={cn(
|
|
67
|
+
'group grid cursor-pointer outline-none',
|
|
68
|
+
'focus-visible:ring-2 ring-info/75 ring-offset-1 rounded-sm',
|
|
69
|
+
'transition-colors duration-150',
|
|
70
|
+
{
|
|
71
|
+
'cursor-not-allowed opacity-50': disabled,
|
|
72
|
+
},
|
|
73
|
+
className,
|
|
74
|
+
)}
|
|
75
|
+
>
|
|
76
|
+
<PushPinIcon
|
|
77
|
+
weight="fill"
|
|
78
|
+
className={cn(
|
|
79
|
+
sizeClasses[size],
|
|
80
|
+
'col-start-1 row-start-1 transition-opacity duration-150 pointer-event-none',
|
|
81
|
+
{
|
|
82
|
+
'text-gray-200 dark:text-zinc-100': checked,
|
|
83
|
+
'text-transparent': !checked,
|
|
84
|
+
'group-hover:text-gray-200 dark:group-hover:text-zinc-400': !disabled && !checked,
|
|
85
|
+
},
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
<PushPinIcon
|
|
89
|
+
weight="regular"
|
|
90
|
+
className={cn(sizeClasses[size], 'col-start-1 row-start-1 pointer-event-none', {
|
|
91
|
+
'text-gray-200 dark:text-zinc-100': checked,
|
|
92
|
+
'text-gray-200 group-hover:text-gray-200 dark:text-zinc-400 group-hover:dark:text-zinc-400':
|
|
93
|
+
!checked && !disabled,
|
|
94
|
+
'text-gray-200 dark:text-zinc-400': !checked && disabled,
|
|
95
|
+
})}
|
|
96
|
+
/>
|
|
97
|
+
</button>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './progress-bar'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '../helpers'
|
|
3
|
+
|
|
4
|
+
export type ProgressBarSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
5
|
+
|
|
6
|
+
export type ProgressBarProps = {
|
|
7
|
+
progress: number
|
|
8
|
+
size?: ProgressBarSize
|
|
9
|
+
className?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ProgressBar = ({ progress, size = 'lg', className }: ProgressBarProps) => {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
aria-valuemax={100}
|
|
16
|
+
aria-valuemin={0}
|
|
17
|
+
aria-valuenow={Math.round(progress)}
|
|
18
|
+
aria-valuetext={`${Math.round(progress)}%`}
|
|
19
|
+
role="progressbar"
|
|
20
|
+
className={cn('relative', className)}
|
|
21
|
+
>
|
|
22
|
+
<div
|
|
23
|
+
data-track
|
|
24
|
+
data-complete={progress >= 100}
|
|
25
|
+
className={cn(
|
|
26
|
+
'flex overflow-hidden rounded-full',
|
|
27
|
+
'border border-gray-200 dark:border-zinc-800 bg-gray-100 dark:bg-zinc-900',
|
|
28
|
+
{
|
|
29
|
+
'h-1': size === 'xs',
|
|
30
|
+
'h-1.5': size === 'sm',
|
|
31
|
+
'h-2': size === 'md',
|
|
32
|
+
'h-4': size === 'lg',
|
|
33
|
+
},
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
data-indicator
|
|
38
|
+
data-complete={progress >= 100}
|
|
39
|
+
style={{ width: `${Math.min(Math.max(progress, 0), 100)}%` }}
|
|
40
|
+
className={cn(
|
|
41
|
+
'rounded-r-full transition-[width] duration-300 ease-out',
|
|
42
|
+
'bg-white dark:bg-zinc-600',
|
|
43
|
+
)}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './quantity-input'
|