sheerly 0.0.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/README.md +3 -0
- package/lib/accordion.css +1 -0
- package/lib/accordion.d.ts +6 -0
- package/lib/alert.css +1 -0
- package/lib/alert.d.ts +8 -0
- package/lib/avatar.css +1 -0
- package/lib/avatar.d.ts +13 -0
- package/lib/badge.css +1 -0
- package/lib/badge.d.ts +5 -0
- package/lib/breadcrumb.css +1 -0
- package/lib/breadcrumb.d.ts +8 -0
- package/lib/button.css +1 -0
- package/lib/button.d.ts +7 -0
- package/lib/card.css +1 -0
- package/lib/card.d.ts +14 -0
- package/lib/carousel.css +1 -0
- package/lib/carousel.d.ts +21 -0
- package/lib/checkbox.css +1 -0
- package/lib/checkbox.d.ts +3 -0
- package/lib/cn.d.ts +5 -0
- package/lib/dialog.css +1 -0
- package/lib/dialog.d.ts +15 -0
- package/lib/dropdown-menu.css +1 -0
- package/lib/dropdown-menu.d.ts +49 -0
- package/lib/hover-card.css +1 -0
- package/lib/hover-card.d.ts +7 -0
- package/lib/index.css +1 -0
- package/lib/index.d.ts +33 -0
- package/lib/index.js +652 -0
- package/lib/input.css +1 -0
- package/lib/input.d.ts +5 -0
- package/lib/kbd.css +1 -0
- package/lib/kbd.d.ts +5 -0
- package/lib/label.css +1 -0
- package/lib/label.d.ts +5 -0
- package/lib/pagination.css +1 -0
- package/lib/pagination.d.ts +17 -0
- package/lib/popover.css +1 -0
- package/lib/popover.d.ts +7 -0
- package/lib/progress.css +1 -0
- package/lib/progress.d.ts +6 -0
- package/lib/radio.css +1 -0
- package/lib/radio.d.ts +5 -0
- package/lib/root.css +1 -0
- package/lib/select.css +1 -0
- package/lib/select.d.ts +10 -0
- package/lib/separator.css +1 -0
- package/lib/separator.d.ts +5 -0
- package/lib/sheet.css +1 -0
- package/lib/sheet.d.ts +16 -0
- package/lib/skeleton.css +1 -0
- package/lib/skeleton.d.ts +3 -0
- package/lib/slider.css +1 -0
- package/lib/slider.d.ts +3 -0
- package/lib/spinner.css +1 -0
- package/lib/spinner.d.ts +10 -0
- package/lib/switch.css +1 -0
- package/lib/switch.d.ts +5 -0
- package/lib/table.css +1 -0
- package/lib/table.d.ts +5 -0
- package/lib/tabs.css +1 -0
- package/lib/tabs.d.ts +13 -0
- package/lib/textarea.css +1 -0
- package/lib/textarea.d.ts +3 -0
- package/lib/toggle.css +1 -0
- package/lib/toggle.d.ts +16 -0
- package/lib/tooltip.css +1 -0
- package/lib/tooltip.d.ts +7 -0
- package/lib/typography.css +1 -0
- package/lib/typography.d.ts +9 -0
- package/package.json +33 -0
- package/src/accordion.css +108 -0
- package/src/accordion.tsx +15 -0
- package/src/alert.css +126 -0
- package/src/alert.tsx +25 -0
- package/src/avatar.css +75 -0
- package/src/avatar.tsx +30 -0
- package/src/badge.css +71 -0
- package/src/badge.tsx +19 -0
- package/src/breadcrumb.css +166 -0
- package/src/breadcrumb.tsx +71 -0
- package/src/button.css +214 -0
- package/src/button.tsx +30 -0
- package/src/card.css +99 -0
- package/src/card.tsx +41 -0
- package/src/carousel.css +183 -0
- package/src/carousel.tsx +30 -0
- package/src/checkbox.css +56 -0
- package/src/checkbox.tsx +10 -0
- package/src/cn.ts +7 -0
- package/src/dialog.css +175 -0
- package/src/dialog.tsx +47 -0
- package/src/dropdown-menu.css +257 -0
- package/src/dropdown-menu.tsx +88 -0
- package/src/hover-card.css +75 -0
- package/src/hover-card.tsx +16 -0
- package/src/index.css +33 -0
- package/src/index.ts +37 -0
- package/src/input.css +62 -0
- package/src/input.tsx +10 -0
- package/src/kbd.css +32 -0
- package/src/kbd.tsx +14 -0
- package/src/label.css +17 -0
- package/src/label.tsx +10 -0
- package/src/pagination.css +150 -0
- package/src/pagination.tsx +95 -0
- package/src/popover.css +65 -0
- package/src/popover.tsx +16 -0
- package/src/progress.css +79 -0
- package/src/progress.tsx +19 -0
- package/src/radio.css +61 -0
- package/src/radio.tsx +14 -0
- package/src/root.css +78 -0
- package/src/select.css +164 -0
- package/src/select.tsx +22 -0
- package/src/separator.css +14 -0
- package/src/separator.tsx +10 -0
- package/src/sheet.css +225 -0
- package/src/sheet.tsx +48 -0
- package/src/skeleton.css +15 -0
- package/src/skeleton.tsx +8 -0
- package/src/slider.css +68 -0
- package/src/slider.tsx +8 -0
- package/src/spinner.css +43 -0
- package/src/spinner.tsx +24 -0
- package/src/switch.css +79 -0
- package/src/switch.tsx +18 -0
- package/src/table.css +77 -0
- package/src/table.tsx +14 -0
- package/src/tabs.css +110 -0
- package/src/tabs.tsx +38 -0
- package/src/textarea.css +38 -0
- package/src/textarea.tsx +8 -0
- package/src/toggle.css +148 -0
- package/src/toggle.tsx +47 -0
- package/src/tooltip.css +60 -0
- package/src/tooltip.tsx +16 -0
- package/src/types.d.ts +42 -0
- package/src/typography.css +176 -0
- package/src/typography.tsx +13 -0
package/src/select.css
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* Select — native <select> with the customizable-select API (appearance: base-select) */
|
|
2
|
+
.sheerly-select,
|
|
3
|
+
.sheerly-select::picker(select) {
|
|
4
|
+
appearance: base-select;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/* .sheerly-select is the trigger button */
|
|
8
|
+
.sheerly-select {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
width: fit-content;
|
|
11
|
+
align-items: center;
|
|
12
|
+
gap: 8px;
|
|
13
|
+
border-radius: var(--sheerly-radius-lg);
|
|
14
|
+
border: 1px solid var(--sheerly-input);
|
|
15
|
+
background-color: transparent;
|
|
16
|
+
padding: 0 12px;
|
|
17
|
+
font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
font-weight: 400;
|
|
20
|
+
line-height: 20px;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
color: var(--sheerly-foreground);
|
|
23
|
+
|
|
24
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
25
|
+
outline: none;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
|
|
28
|
+
/* Default size (32px) */
|
|
29
|
+
height: 32px;
|
|
30
|
+
|
|
31
|
+
&:focus-visible {
|
|
32
|
+
border-color: var(--sheerly-ring);
|
|
33
|
+
box-shadow: var(--sheerly-focus-ring);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&:disabled {
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
opacity: 0.5;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&[aria-invalid="true"] {
|
|
42
|
+
border-color: var(--sheerly-destructive);
|
|
43
|
+
box-shadow: 0 0 0 3px oklch(from var(--sheerly-destructive) l c h / 20%);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:invalid,
|
|
47
|
+
&[data-placeholder] {
|
|
48
|
+
color: var(--sheerly-muted-foreground);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&[data-size="sm"] {
|
|
52
|
+
height: 32px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&[data-size="lg"] {
|
|
56
|
+
height: 40px;
|
|
57
|
+
font-size: 16px;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Picker icon (thin chevron, replacing the default triangle glyph) */
|
|
62
|
+
.sheerly-select::picker-icon {
|
|
63
|
+
width: 16px;
|
|
64
|
+
height: 16px;
|
|
65
|
+
color: var(--sheerly-muted-foreground);
|
|
66
|
+
content: "";
|
|
67
|
+
background-color: currentColor;
|
|
68
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
69
|
+
transition: rotate 0.2s ease;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.sheerly-select:open::picker-icon {
|
|
73
|
+
rotate: 180deg;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Displays the current value in the trigger */
|
|
77
|
+
.sheerly-select selectedcontent {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 8px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* An empty-value option acts as the placeholder */
|
|
84
|
+
.sheerly-select option[value=""] {
|
|
85
|
+
color: var(--sheerly-muted-foreground);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* The drop-down panel (::picker) */
|
|
89
|
+
.sheerly-select::picker(select) {
|
|
90
|
+
border: 1px solid var(--sheerly-border);
|
|
91
|
+
border-radius: var(--sheerly-radius-xl);
|
|
92
|
+
background-color: var(--sheerly-popover);
|
|
93
|
+
color: var(--sheerly-popover-foreground);
|
|
94
|
+
box-shadow: 0 4px 16px -4px oklch(0 0 0 / 8%), 0 1px 2px oklch(0 0 0 / 4%);
|
|
95
|
+
padding: 4px;
|
|
96
|
+
max-height: 256px;
|
|
97
|
+
overflow-y: auto;
|
|
98
|
+
opacity: 0;
|
|
99
|
+
transition: all 0.2s allow-discrete;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.sheerly-select:open::picker(select) {
|
|
103
|
+
opacity: 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@starting-style {
|
|
107
|
+
.sheerly-select:open::picker(select) {
|
|
108
|
+
opacity: 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Rows inside the drop-down picker */
|
|
113
|
+
.sheerly-select option {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 8px;
|
|
117
|
+
border-radius: var(--sheerly-radius-md);
|
|
118
|
+
padding: 6px 8px;
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
line-height: 20px;
|
|
121
|
+
cursor: default;
|
|
122
|
+
user-select: none;
|
|
123
|
+
transition: background-color 0.1s ease;
|
|
124
|
+
|
|
125
|
+
&:hover,
|
|
126
|
+
&:focus {
|
|
127
|
+
background-color: var(--sheerly-accent);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&:checked {
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
&:disabled {
|
|
135
|
+
color: var(--sheerly-muted-foreground);
|
|
136
|
+
pointer-events: none;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Checkmark on selected option */
|
|
141
|
+
.sheerly-select option::checkmark {
|
|
142
|
+
color: var(--sheerly-foreground);
|
|
143
|
+
order: 1;
|
|
144
|
+
margin-left: auto;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.sheerly-select optgroup {
|
|
148
|
+
padding: 4px 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.sheerly-select optgroup legend {
|
|
152
|
+
padding: 4px 8px;
|
|
153
|
+
font-size: 12px;
|
|
154
|
+
line-height: 16px;
|
|
155
|
+
font-weight: 500;
|
|
156
|
+
color: var(--sheerly-muted-foreground);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Separator between groups */
|
|
160
|
+
.sheerly-select optgroup + optgroup {
|
|
161
|
+
border-top: 1px solid var(--sheerly-border);
|
|
162
|
+
margin-top: 4px;
|
|
163
|
+
padding-top: 4px;
|
|
164
|
+
}
|
package/src/select.tsx
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SelectHTMLAttributes } from 'react'
|
|
2
|
+
import { cn } from './cn'
|
|
3
|
+
|
|
4
|
+
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
5
|
+
'data-size'?: 'sm' | 'default' | 'lg'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Uses the customizable-select API. Requires
|
|
10
|
+
* `<button><selectedcontent></selectedcontent></button>` as the first child,
|
|
11
|
+
* which this wrapper injects before the consumer's <option>s.
|
|
12
|
+
*/
|
|
13
|
+
export function Select({ className, children, ...props }: SelectProps) {
|
|
14
|
+
return (
|
|
15
|
+
<select className={cn('sheerly-select', className)} {...props}>
|
|
16
|
+
<button>
|
|
17
|
+
<selectedcontent />
|
|
18
|
+
</button>
|
|
19
|
+
{children}
|
|
20
|
+
</select>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* Separator — thin dividing line */
|
|
2
|
+
.sheerly-separator {
|
|
3
|
+
flex-shrink: 0;
|
|
4
|
+
background-color: var(--sheerly-border);
|
|
5
|
+
|
|
6
|
+
/* Horizontal (default) */
|
|
7
|
+
height: 1px;
|
|
8
|
+
width: 100%;
|
|
9
|
+
|
|
10
|
+
&[data-orientation="vertical"] {
|
|
11
|
+
height: 100%;
|
|
12
|
+
width: 1px;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react'
|
|
2
|
+
import { cn } from './cn'
|
|
3
|
+
|
|
4
|
+
export interface SeparatorProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
'data-orientation'?: 'horizontal' | 'vertical'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function Separator({ className, ...props }: SeparatorProps) {
|
|
9
|
+
return <div className={cn('sheerly-separator', className)} {...props} />
|
|
10
|
+
}
|
package/src/sheet.css
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Sheet — a native <dialog> that slides in from an edge of the screen.
|
|
3
|
+
* Extends the Dialog pattern; open via invoker command (command="show-modal")
|
|
4
|
+
* or `.showModal()`.
|
|
5
|
+
*
|
|
6
|
+
* Side via data-side: right (default) | left | top | bottom.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <dialog id="s" class="sheerly-sheet" data-side="right" closedby="any">
|
|
10
|
+
* <button class="sheerly-sheet-close" commandfor="s" command="close" aria-label="Close"></button>
|
|
11
|
+
* <div class="sheerly-sheet-header">
|
|
12
|
+
* <h3 class="sheerly-sheet-title">Edit profile</h3>
|
|
13
|
+
* <p class="sheerly-sheet-description">Make changes here.</p>
|
|
14
|
+
* </div>
|
|
15
|
+
* <div class="sheerly-sheet-body">…</div>
|
|
16
|
+
* <div class="sheerly-sheet-footer">…</div>
|
|
17
|
+
* </dialog>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
.sheerly-sheet {
|
|
21
|
+
position: fixed;
|
|
22
|
+
z-index: 50;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 16px;
|
|
25
|
+
border: none;
|
|
26
|
+
background-color: var(--sheerly-background);
|
|
27
|
+
color: var(--sheerly-foreground);
|
|
28
|
+
box-shadow: 0 0 0 1px oklch(from var(--sheerly-foreground) l c h / 10%);
|
|
29
|
+
padding: 0;
|
|
30
|
+
margin: 0;
|
|
31
|
+
outline: none;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
font-size: 14px;
|
|
34
|
+
line-height: 20px;
|
|
35
|
+
|
|
36
|
+
/* Closed state */
|
|
37
|
+
display: none;
|
|
38
|
+
opacity: 0;
|
|
39
|
+
max-height: 100dvh;
|
|
40
|
+
max-width: 100vw;
|
|
41
|
+
|
|
42
|
+
transition:
|
|
43
|
+
translate 0.3s cubic-bezier(0.32, 0.72, 0, 1),
|
|
44
|
+
opacity 0.3s ease,
|
|
45
|
+
display 0.3s allow-discrete,
|
|
46
|
+
overlay 0.3s allow-discrete;
|
|
47
|
+
|
|
48
|
+
&[open] {
|
|
49
|
+
display: flex;
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&::backdrop {
|
|
54
|
+
background-color: oklch(0 0 0 / 10%);
|
|
55
|
+
backdrop-filter: blur(1px);
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transition: opacity 0.3s ease, overlay 0.3s allow-discrete, display 0.3s allow-discrete;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&[open]::backdrop {
|
|
61
|
+
opacity: 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@starting-style {
|
|
65
|
+
&[open]::backdrop {
|
|
66
|
+
opacity: 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* --- Sides ------------------------------------------------------------ */
|
|
71
|
+
|
|
72
|
+
/* Right (default) */
|
|
73
|
+
inset: 0 0 0 auto;
|
|
74
|
+
height: 100dvh;
|
|
75
|
+
width: 100%;
|
|
76
|
+
max-width: 384px;
|
|
77
|
+
translate: 100% 0;
|
|
78
|
+
|
|
79
|
+
&[open] {
|
|
80
|
+
translate: 0 0;
|
|
81
|
+
|
|
82
|
+
@starting-style {
|
|
83
|
+
translate: 100% 0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&[data-side="left"] {
|
|
88
|
+
inset: 0 auto 0 0;
|
|
89
|
+
height: 100dvh;
|
|
90
|
+
width: 100%;
|
|
91
|
+
max-width: 384px;
|
|
92
|
+
translate: -100% 0;
|
|
93
|
+
|
|
94
|
+
&[open] {
|
|
95
|
+
translate: 0 0;
|
|
96
|
+
|
|
97
|
+
@starting-style {
|
|
98
|
+
translate: -100% 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&[data-side="top"] {
|
|
104
|
+
inset: 0 0 auto 0;
|
|
105
|
+
width: 100%;
|
|
106
|
+
max-width: none;
|
|
107
|
+
height: auto;
|
|
108
|
+
translate: 0 -100%;
|
|
109
|
+
|
|
110
|
+
&[open] {
|
|
111
|
+
translate: 0 0;
|
|
112
|
+
|
|
113
|
+
@starting-style {
|
|
114
|
+
translate: 0 -100%;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&[data-side="bottom"] {
|
|
120
|
+
inset: auto 0 0 0;
|
|
121
|
+
width: 100%;
|
|
122
|
+
max-width: none;
|
|
123
|
+
height: auto;
|
|
124
|
+
translate: 0 100%;
|
|
125
|
+
|
|
126
|
+
&[open] {
|
|
127
|
+
translate: 0 0;
|
|
128
|
+
|
|
129
|
+
@starting-style {
|
|
130
|
+
translate: 0 100%;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.sheerly-sheet-header {
|
|
137
|
+
display: flex;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
gap: 4px;
|
|
140
|
+
padding: 16px;
|
|
141
|
+
flex-shrink: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.sheerly-sheet-title {
|
|
145
|
+
font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
|
|
146
|
+
font-size: 16px;
|
|
147
|
+
font-weight: 500;
|
|
148
|
+
line-height: 1;
|
|
149
|
+
color: var(--sheerly-foreground);
|
|
150
|
+
margin: 0;
|
|
151
|
+
padding-right: 32px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.sheerly-sheet-description {
|
|
155
|
+
font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
|
|
156
|
+
font-size: 14px;
|
|
157
|
+
font-weight: 400;
|
|
158
|
+
line-height: 20px;
|
|
159
|
+
color: var(--sheerly-muted-foreground);
|
|
160
|
+
margin: 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Scrollable content area */
|
|
164
|
+
.sheerly-sheet-body {
|
|
165
|
+
padding: 0 16px;
|
|
166
|
+
overflow-y: auto;
|
|
167
|
+
flex: 1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.sheerly-sheet-footer {
|
|
171
|
+
display: flex;
|
|
172
|
+
flex-direction: column;
|
|
173
|
+
gap: 8px;
|
|
174
|
+
padding: 16px;
|
|
175
|
+
flex-shrink: 0;
|
|
176
|
+
margin-top: auto;
|
|
177
|
+
|
|
178
|
+
& > * {
|
|
179
|
+
width: 100%;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.sheerly-sheet-close {
|
|
184
|
+
position: absolute;
|
|
185
|
+
top: 12px;
|
|
186
|
+
right: 16px;
|
|
187
|
+
display: inline-flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
justify-content: center;
|
|
190
|
+
width: 28px;
|
|
191
|
+
height: 28px;
|
|
192
|
+
padding: 0;
|
|
193
|
+
border: none;
|
|
194
|
+
border-radius: var(--sheerly-radius-md);
|
|
195
|
+
background-color: transparent;
|
|
196
|
+
color: var(--sheerly-muted-foreground);
|
|
197
|
+
cursor: pointer;
|
|
198
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
199
|
+
outline: none;
|
|
200
|
+
z-index: 1;
|
|
201
|
+
|
|
202
|
+
&:hover {
|
|
203
|
+
background-color: var(--sheerly-muted);
|
|
204
|
+
color: var(--sheerly-foreground);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
&:focus-visible {
|
|
208
|
+
border-color: var(--sheerly-ring);
|
|
209
|
+
box-shadow: var(--sheerly-focus-ring);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
& svg {
|
|
213
|
+
width: 16px;
|
|
214
|
+
height: 16px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Default close (X) icon when no child is provided */
|
|
218
|
+
&:empty::before {
|
|
219
|
+
content: "";
|
|
220
|
+
width: 16px;
|
|
221
|
+
height: 16px;
|
|
222
|
+
background-color: currentColor;
|
|
223
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18'/%3E%3Cpath d='m6 6 12 12'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
224
|
+
}
|
|
225
|
+
}
|
package/src/sheet.tsx
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, DialogHTMLAttributes, HTMLAttributes } from 'react'
|
|
2
|
+
import { cn } from './cn'
|
|
3
|
+
|
|
4
|
+
export interface SheetProps extends DialogHTMLAttributes<HTMLDialogElement> {
|
|
5
|
+
'data-side'?: 'right' | 'left' | 'top' | 'bottom'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Native <dialog> that slides in from an edge. Open via invoker command
|
|
10
|
+
* (`command="show-modal"`) or `.showModal()`. Add `closedby="any"` for
|
|
11
|
+
* light-dismiss.
|
|
12
|
+
*/
|
|
13
|
+
export function Sheet({ className, ...props }: SheetProps) {
|
|
14
|
+
return <dialog className={cn('sheerly-sheet', className)} {...props} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function SheetHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
|
|
18
|
+
return <div className={cn('sheerly-sheet-header', className)} {...props} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function SheetTitle({ className, ...props }: HTMLAttributes<HTMLHeadingElement>) {
|
|
22
|
+
return <h3 className={cn('sheerly-sheet-title', className)} {...props} />
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function SheetDescription({
|
|
26
|
+
className,
|
|
27
|
+
...props
|
|
28
|
+
}: HTMLAttributes<HTMLParagraphElement>) {
|
|
29
|
+
return <p className={cn('sheerly-sheet-description', className)} {...props} />
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function SheetBody({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
|
|
33
|
+
return <div className={cn('sheerly-sheet-body', className)} {...props} />
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function SheetFooter({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
|
|
37
|
+
return <div className={cn('sheerly-sheet-footer', className)} {...props} />
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function SheetClose({
|
|
41
|
+
className,
|
|
42
|
+
type = 'button',
|
|
43
|
+
...props
|
|
44
|
+
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
45
|
+
return (
|
|
46
|
+
<button type={type} className={cn('sheerly-sheet-close', className)} {...props} />
|
|
47
|
+
)
|
|
48
|
+
}
|
package/src/skeleton.css
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Skeleton — pulsing loading placeholder */
|
|
2
|
+
.sheerly-skeleton {
|
|
3
|
+
border-radius: var(--sheerly-radius-sm);
|
|
4
|
+
background-color: var(--sheerly-muted);
|
|
5
|
+
animation: sheerly-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@keyframes sheerly-pulse {
|
|
9
|
+
0%, 100% {
|
|
10
|
+
opacity: 1;
|
|
11
|
+
}
|
|
12
|
+
50% {
|
|
13
|
+
opacity: 0.5;
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/skeleton.tsx
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react'
|
|
2
|
+
import { cn } from './cn'
|
|
3
|
+
|
|
4
|
+
export type SkeletonProps = HTMLAttributes<HTMLDivElement>
|
|
5
|
+
|
|
6
|
+
export function Skeleton({ className, ...props }: SkeletonProps) {
|
|
7
|
+
return <div className={cn('sheerly-skeleton', className)} {...props} />
|
|
8
|
+
}
|
package/src/slider.css
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Slider — native <input type="range"> (WebKit pseudo-elements only) */
|
|
2
|
+
.sheerly-slider {
|
|
3
|
+
-webkit-appearance: none;
|
|
4
|
+
appearance: none;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 4px;
|
|
7
|
+
background-color: var(--sheerly-border);
|
|
8
|
+
border-radius: var(--sheerly-radius-full);
|
|
9
|
+
outline: none;
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
margin: 8px 0;
|
|
12
|
+
|
|
13
|
+
&::-webkit-slider-runnable-track {
|
|
14
|
+
-webkit-appearance: none;
|
|
15
|
+
appearance: none;
|
|
16
|
+
height: 4px;
|
|
17
|
+
background-color: var(--sheerly-border);
|
|
18
|
+
border-radius: var(--sheerly-radius-full);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&::-webkit-slider-thumb {
|
|
22
|
+
-webkit-appearance: none;
|
|
23
|
+
appearance: none;
|
|
24
|
+
width: 16px;
|
|
25
|
+
height: 16px;
|
|
26
|
+
background-color: #ffffff;
|
|
27
|
+
border: 2px solid var(--sheerly-foreground);
|
|
28
|
+
border-radius: 50%;
|
|
29
|
+
cursor: grab;
|
|
30
|
+
margin-top: -6px;
|
|
31
|
+
transform: scale(0.85);
|
|
32
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:active::-webkit-slider-thumb {
|
|
36
|
+
transform: scale(1);
|
|
37
|
+
cursor: grabbing;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:focus-visible::-webkit-slider-thumb {
|
|
41
|
+
transform: scale(1);
|
|
42
|
+
box-shadow: var(--sheerly-focus-ring);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&:disabled {
|
|
46
|
+
opacity: 0.5;
|
|
47
|
+
cursor: not-allowed;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&:disabled::-webkit-slider-thumb {
|
|
51
|
+
cursor: not-allowed;
|
|
52
|
+
border-color: var(--sheerly-input);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Dark mode */
|
|
57
|
+
.dark .sheerly-slider {
|
|
58
|
+
background-color: var(--sheerly-border);
|
|
59
|
+
|
|
60
|
+
&::-webkit-slider-runnable-track {
|
|
61
|
+
background-color: var(--sheerly-border);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&::-webkit-slider-thumb {
|
|
65
|
+
background-color: var(--sheerly-foreground);
|
|
66
|
+
border-color: var(--sheerly-foreground);
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/slider.tsx
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { InputHTMLAttributes } from 'react'
|
|
2
|
+
import { cn } from './cn'
|
|
3
|
+
|
|
4
|
+
export type SliderProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>
|
|
5
|
+
|
|
6
|
+
export function Slider({ className, ...props }: SliderProps) {
|
|
7
|
+
return <input type="range" className={cn('sheerly-slider', className)} {...props} />
|
|
8
|
+
}
|
package/src/spinner.css
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Spinner — a spinning loading indicator.
|
|
3
|
+
*
|
|
4
|
+
* The spinner glyph is embedded in CSS (a masked ::before), so no <svg> child
|
|
5
|
+
* is required — a bare element works:
|
|
6
|
+
*
|
|
7
|
+
* <span class="sheerly-spinner" role="status" aria-label="Loading"></span>
|
|
8
|
+
*
|
|
9
|
+
* It inherits the current text color.
|
|
10
|
+
*/
|
|
11
|
+
.sheerly-spinner {
|
|
12
|
+
display: inline-block;
|
|
13
|
+
width: 16px;
|
|
14
|
+
height: 16px;
|
|
15
|
+
flex-shrink: 0;
|
|
16
|
+
background-color: currentColor;
|
|
17
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-6.219-8.56'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
18
|
+
animation: sheerly-spin 1s linear infinite;
|
|
19
|
+
|
|
20
|
+
&[data-size="sm"] {
|
|
21
|
+
width: 12px;
|
|
22
|
+
height: 12px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&[data-size="md"] {
|
|
26
|
+
width: 20px;
|
|
27
|
+
height: 20px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&[data-size="lg"] {
|
|
31
|
+
width: 24px;
|
|
32
|
+
height: 24px;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@keyframes sheerly-spin {
|
|
37
|
+
from {
|
|
38
|
+
rotate: 0deg;
|
|
39
|
+
}
|
|
40
|
+
to {
|
|
41
|
+
rotate: 360deg;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/spinner.tsx
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react'
|
|
2
|
+
import { cn } from './cn'
|
|
3
|
+
|
|
4
|
+
export interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
'data-size'?: 'sm' | 'default' | 'md' | 'lg'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Loading spinner. The glyph is embedded in CSS (masked ::before), so no SVG
|
|
10
|
+
* child is needed — this renders a bare <span>. It inherits the current text
|
|
11
|
+
* color. Pass an <svg> child if you prefer to ship your own icon.
|
|
12
|
+
*/
|
|
13
|
+
export function Spinner({ className, children, ...props }: SpinnerProps) {
|
|
14
|
+
return (
|
|
15
|
+
<span
|
|
16
|
+
className={cn('sheerly-spinner', className)}
|
|
17
|
+
role="status"
|
|
18
|
+
aria-label="Loading"
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</span>
|
|
23
|
+
)
|
|
24
|
+
}
|