Skip to content

Commit ed43046

Browse files
committed
[shadcn] Restore context file
1 parent 8aa9ecc commit ed43046

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* eslint-disable @typescript-eslint/no-empty-object-type */
2+
import { type Component, getContext, setContext } from 'svelte';
3+
import type {
4+
HTMLButtonAttributes,
5+
HTMLInputAttributes,
6+
HTMLTextareaAttributes
7+
} from 'svelte/elements';
8+
import type {
9+
Calendar,
10+
Checkbox,
11+
Label,
12+
RadioGroup,
13+
Select,
14+
Slider,
15+
Switch,
16+
WithElementRef,
17+
WithoutChild,
18+
WithoutChildrenOrChild
19+
} from 'bits-ui';
20+
import { Popover } from 'bits-ui';
21+
22+
export type CalendarProps = WithoutChildrenOrChild<Calendar.RootProps>;
23+
24+
export interface ThemeComponents {
25+
Button: Component<HTMLButtonAttributes>;
26+
// @ts-expect-error too complex
27+
Calendar: Component<CalendarProps, {}, 'value' | 'placeholder' | 'ref'>;
28+
Checkbox: Component<WithoutChildrenOrChild<Checkbox.RootProps>, {}, 'checked' | 'indeterminate' | 'ref'>;
29+
Input: Component<WithElementRef<HTMLInputAttributes>, {}, 'value' | 'ref'>;
30+
FilesInput: Component<
31+
HTMLInputAttributes & {
32+
files?: FileList;
33+
},
34+
{},
35+
'files'
36+
>;
37+
Label: Component<Label.RootProps>;
38+
Popover: Component<Popover.ContentProps>;
39+
// @deprecate (for search reasons)
40+
// TODO: Make this components required in next major
41+
PopoverTrigger?: Component<Popover.TriggerProps>;
42+
PopoverContent?: Component<Popover.ContentProps>;
43+
RadioGroup: Component<RadioGroup.RootProps, {}, 'value' | 'ref'>;
44+
RadioGroupItem: Component<WithoutChildrenOrChild<RadioGroup.ItemProps>>;
45+
Select: Component<Select.RootProps, {}, 'value' | 'open'>;
46+
SelectItem: Component<WithoutChild<Select.ItemProps>>;
47+
SelectTrigger: Component<WithoutChild<Select.TriggerProps>>;
48+
SelectContent: Component<WithoutChild<Select.ContentProps>>;
49+
Slider: Component<WithoutChildrenOrChild<Slider.RootProps>, {}, 'value' | 'ref'>;
50+
Switch: Component<WithoutChildrenOrChild<Switch.RootProps>, {}, 'checked' | 'ref'>;
51+
Textarea: Component<WithElementRef<HTMLTextareaAttributes>, {}, 'value' | 'ref'>;
52+
}
53+
54+
export type DateFormatter = (date: Date) => string;
55+
56+
export interface ThemeContext {
57+
components: ThemeComponents;
58+
formatDate?: DateFormatter;
59+
}
60+
61+
const THEME_CONTEXT = Symbol('theme-context');
62+
63+
export function getThemeContext(): Required<Omit<ThemeContext, 'components'>> & {
64+
components: Required<ThemeComponents>;
65+
} {
66+
return getContext(THEME_CONTEXT);
67+
}
68+
69+
export function setThemeContext(ctx: ThemeContext) {
70+
// TODO: Remove Proxy in next major
71+
const dateTimeFormat = new Intl.DateTimeFormat(undefined, {
72+
year: 'numeric',
73+
month: '2-digit',
74+
day: 'numeric'
75+
});
76+
setContext(THEME_CONTEXT, {
77+
get formatDate() {
78+
return ctx.formatDate ?? ((date) => dateTimeFormat.format(date));
79+
},
80+
get components() {
81+
return new Proxy(ctx.components, {
82+
get(target, prop, receiver) {
83+
switch (prop as keyof ThemeComponents) {
84+
case 'PopoverContent': {
85+
return target.PopoverContent ?? Popover.Content;
86+
}
87+
case 'PopoverTrigger': {
88+
return target.PopoverTrigger ?? Popover.Trigger;
89+
}
90+
default:
91+
return Reflect.get(target, prop, receiver);
92+
}
93+
}
94+
});
95+
}
96+
});
97+
}

0 commit comments

Comments
 (0)