Skip to content

Commit 7b1e5ee

Browse files
committed
refactor: upgrade shadcn-vue components to use reka-ui primitives and add @vueuse/core
- Add @vueuse/core v14.3.0 dependency to marketing app - Upgrade reka-ui from v2.9.6 to v2.9.8 in marketing app - Refactor Input component to use useVModel composable instead of manual event handling - Refactor Switch component to use SwitchRoot/SwitchThumb from reka-ui with proper props forwarding - Update DropdownMenu components to use data attributes, improved styling, and proper slot forwarding - Add destructive variant support to DropdownMenuItem - Add size prop ('sm' |
1 parent 3bd0afc commit 7b1e5ee

24 files changed

Lines changed: 416 additions & 121 deletions

new-deepnotes/apps/marketing/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

new-deepnotes/apps/marketing/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
},
1313
"dependencies": {
1414
"@tailwindcss/vite": "^4.2.4",
15+
"@vueuse/core": "^14.3.0",
1516
"class-variance-authority": "^0.7.1",
1617
"clsx": "^2.1.1",
1718
"marked": "^18.0.4",
18-
"reka-ui": "^2.9.6",
19+
"reka-ui": "^2.9.8",
1920
"shadcn-vue": "^2.6.2",
2021
"tailwind-merge": "^3.5.0",
2122
"tailwindcss": "^4.2.4",
Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
<script setup lang="ts">
2-
import type { HTMLAttributes } from "vue";
3-
import { cn } from "@/lib/utils";
2+
import type { HTMLAttributes } from 'vue'
3+
import { useVModel } from '@vueuse/core'
4+
import { cn } from '@/lib/utils'
45
56
const props = defineProps<{
6-
modelValue?: string | number;
7-
defaultValue?: string | number;
8-
class?: HTMLAttributes["class"];
9-
}>();
7+
defaultValue?: string | number
8+
modelValue?: string | number
9+
class?: HTMLAttributes['class']
10+
}>()
1011
11-
const emit = defineEmits<{
12-
(e: "update:modelValue", payload: string | number): void;
13-
}>();
12+
const emits = defineEmits<{
13+
(e: 'update:modelValue', payload: string | number): void
14+
}>()
1415
15-
function onInput(event: Event) {
16-
const target = event.target as HTMLInputElement;
17-
emit("update:modelValue", target.value);
18-
}
16+
const modelValue = useVModel(props, 'modelValue', emits, {
17+
passive: true,
18+
defaultValue: props.defaultValue,
19+
})
1920
</script>
2021

2122
<template>
2223
<input
23-
:value="modelValue ?? defaultValue"
24+
v-model="modelValue"
2425
data-slot="input"
25-
:class="
26-
cn(
27-
'border-input file:text-foreground placeholder:text-muted-foreground focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
28-
props.class,
29-
)
30-
"
31-
@input="onInput"
32-
/>
26+
:class="cn(
27+
'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
28+
props.class,
29+
)"
30+
>
3331
</template>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { default as Input } from "./Input.vue";
1+
export { default as Input } from './Input.vue'
Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,44 @@
11
<script setup lang="ts">
2-
import { cn } from "@/lib/utils";
2+
import type { SwitchRootEmits, SwitchRootProps } from 'reka-ui'
3+
import type { HTMLAttributes } from 'vue'
4+
import { reactiveOmit } from '@vueuse/core'
5+
import {
6+
SwitchRoot,
7+
SwitchThumb,
8+
useForwardPropsEmits,
9+
} from 'reka-ui'
10+
import { cn } from '@/lib/utils'
311
4-
const props = defineProps<{
5-
modelValue?: boolean;
6-
class?: string;
7-
}>();
12+
const props = withDefaults(defineProps<SwitchRootProps & {
13+
class?: HTMLAttributes['class']
14+
size?: 'sm' | 'default'
15+
}>(), {
16+
size: 'default',
17+
})
818
9-
const emit = defineEmits<{
10-
(e: "update:modelValue", value: boolean): void;
11-
}>();
19+
const emits = defineEmits<SwitchRootEmits>()
1220
13-
function toggle() {
14-
emit("update:modelValue", !props.modelValue);
15-
}
21+
const delegatedProps = reactiveOmit(props, 'class', 'size')
22+
23+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
1624
</script>
1725

1826
<template>
19-
<button
20-
type="button"
21-
role="switch"
22-
:aria-checked="modelValue"
23-
:class="
24-
cn(
25-
'relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
26-
modelValue ? 'bg-primary' : 'bg-muted',
27-
props.class,
28-
)
29-
"
30-
@click="toggle"
27+
<SwitchRoot
28+
v-slot="slotProps"
29+
data-slot="switch"
30+
:data-size="size"
31+
v-bind="forwarded"
32+
:class="cn(
33+
'data-checked:bg-primary data-unchecked:bg-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-disabled:cursor-not-allowed data-disabled:opacity-50',
34+
props.class,
35+
)"
3136
>
32-
<span
33-
:class="
34-
cn(
35-
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-background shadow ring-0 transition duration-200 ease-in-out',
36-
modelValue ? 'translate-x-5' : 'translate-x-0',
37-
)
38-
"
39-
/>
40-
</button>
37+
<SwitchThumb
38+
data-slot="switch-thumb"
39+
class="bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground rounded-full group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 pointer-events-none block ring-0 transition-transform"
40+
>
41+
<slot name="thumb" v-bind="slotProps" />
42+
</SwitchThumb>
43+
</SwitchRoot>
4144
</template>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { default as Switch } from "./Switch.vue";
1+
export { default as Switch } from './Switch.vue'

new-deepnotes/apps/web/src/components/ui/checkbox/Checkbox.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import type { CheckboxRootEmits, CheckboxRootProps } from 'reka-ui'
33
44
import type { HTMLAttributes } from 'vue'
5-
import { reactiveOmit } from '@vueuse/core'
65
import { CheckIcon } from '@lucide/vue'
6+
import { reactiveOmit } from '@vueuse/core'
77
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'reka-ui'
88
import { cn } from '@/lib/utils'
99

new-deepnotes/apps/web/src/components/ui/dropdown-menu/DropdownMenu.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ const forwarded = useForwardPropsEmits(props, emits)
99
</script>
1010

1111
<template>
12-
<DropdownMenuRoot v-bind="forwarded">
13-
<slot />
12+
<DropdownMenuRoot
13+
v-slot="slotProps"
14+
data-slot="dropdown-menu"
15+
v-bind="forwarded"
16+
>
17+
<slot v-bind="slotProps" />
1418
</DropdownMenuRoot>
1519
</template>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script setup lang="ts">
2+
import type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from 'reka-ui'
3+
4+
import type { HTMLAttributes } from 'vue'
5+
import { CheckIcon } from '@lucide/vue'
6+
import { reactiveOmit } from '@vueuse/core'
7+
import {
8+
DropdownMenuCheckboxItem,
9+
DropdownMenuItemIndicator,
10+
useForwardPropsEmits,
11+
} from 'reka-ui'
12+
import { cn } from '@/lib/utils'
13+
14+
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
15+
const emits = defineEmits<DropdownMenuCheckboxItemEmits>()
16+
17+
const delegatedProps = reactiveOmit(props, 'class')
18+
19+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
20+
</script>
21+
22+
<template>
23+
<DropdownMenuCheckboxItem
24+
data-slot="dropdown-menu-checkbox-item"
25+
v-bind="forwarded"
26+
:class="cn(
27+
'focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*=size-])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
28+
props.class,
29+
)"
30+
>
31+
<span
32+
class="absolute right-2 flex items-center justify-center pointer-events-none"
33+
data-slot="dropdown-menu-checkbox-item-indicator"
34+
>
35+
<DropdownMenuItemIndicator>
36+
<slot name="indicator-icon">
37+
<CheckIcon />
38+
</slot>
39+
</DropdownMenuItemIndicator>
40+
</span>
41+
<slot />
42+
</DropdownMenuCheckboxItem>
43+
</template>

new-deepnotes/apps/web/src/components/ui/dropdown-menu/DropdownMenuContent.vue

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
<script setup lang="ts">
22
import type { DropdownMenuContentEmits, DropdownMenuContentProps } from 'reka-ui'
3-
import { DropdownMenuContent, DropdownMenuPortal, useForwardPropsEmits } from 'reka-ui'
4-
import { computed, useAttrs } from 'vue'
3+
import type { HTMLAttributes } from 'vue'
4+
import { reactiveOmit } from '@vueuse/core'
5+
import {
6+
DropdownMenuContent,
7+
DropdownMenuPortal,
8+
useForwardPropsEmits,
9+
} from 'reka-ui'
10+
import { cn } from '@/lib/utils'
511
6-
defineOptions({ inheritAttrs: false })
7-
8-
const props = withDefaults(defineProps<DropdownMenuContentProps>(), {
9-
sideOffset: 4,
12+
defineOptions({
13+
inheritAttrs: false,
1014
})
15+
16+
const props = withDefaults(
17+
defineProps<DropdownMenuContentProps & { class?: HTMLAttributes['class'] }>(),
18+
{
19+
align: 'start',
20+
sideOffset: 4,
21+
},
22+
)
1123
const emits = defineEmits<DropdownMenuContentEmits>()
1224
13-
const forwarded = useForwardPropsEmits(props, emits)
14-
const attrs = useAttrs()
25+
const delegatedProps = reactiveOmit(props, 'class')
1526
16-
const contentClass = computed(() => {
17-
const base = "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
18-
const extraClass = typeof attrs.class === 'string' ? attrs.class : ''
19-
return extraClass ? `${base} ${extraClass}` : base
20-
})
27+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
2128
</script>
2229

2330
<template>
2431
<DropdownMenuPortal>
25-
<DropdownMenuContent v-bind="forwarded" :class="contentClass" :style="attrs.style as any">
32+
<DropdownMenuContent
33+
data-slot="dropdown-menu-content"
34+
v-bind="{ ...$attrs, ...forwarded }"
35+
:class="cn('data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 cn-menu-translucent z-50 max-h-(--reka-dropdown-menu-content-available-height) w-(--reka-dropdown-menu-trigger-width) origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden', props.class)"
36+
>
2637
<slot />
2738
</DropdownMenuContent>
2839
</DropdownMenuPortal>

0 commit comments

Comments
 (0)