@carlonicora/nextjs-jsonapi 3.3.9 → 3.4.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/dist/{BlockNoteEditor-BYMRWUVG.mjs → BlockNoteEditor-3D5D7YL7.mjs} +2 -2
- package/dist/{BlockNoteEditor-USRMFZGP.js → BlockNoteEditor-F6TGKMTW.js} +9 -9
- package/dist/{BlockNoteEditor-USRMFZGP.js.map → BlockNoteEditor-F6TGKMTW.js.map} +1 -1
- package/dist/billing/index.js +347 -347
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +27 -27
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-CFZW6GU4.mjs → chunk-G5LSXG5K.mjs} +2572 -2482
- package/dist/chunk-G5LSXG5K.mjs.map +1 -0
- package/dist/{chunk-5WFYQE22.js → chunk-KT4CTWCV.js} +825 -735
- package/dist/chunk-KT4CTWCV.js.map +1 -0
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.d.mts +12 -6
- package/dist/components/index.d.ts +12 -6
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.d.mts +13 -1
- package/dist/contexts/index.d.ts +13 -1
- package/dist/contexts/index.js +6 -2
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +5 -1
- package/dist/features/help/index.js +35 -35
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +6 -6
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/tokenusage/index.js +135 -129
- package/dist/features/tokenusage/index.js.map +1 -1
- package/dist/features/tokenusage/index.mjs +74 -68
- package/dist/features/tokenusage/index.mjs.map +1 -1
- package/package.json +2 -2
- package/scripts/check-rtl-classes.mjs +60 -0
- package/scripts/check-rtl-classes.test.ts +25 -0
- package/src/components/TableCellAvatar.tsx +1 -1
- package/src/components/containers/PageContainer.tsx +1 -1
- package/src/components/containers/ReactMarkdownContainer.tsx +20 -4
- package/src/components/containers/RoundPageContainer.tsx +7 -7
- package/src/components/contents/AttributeElement.tsx +1 -1
- package/src/components/editors/BlockNoteDiffInlineContent.tsx +2 -2
- package/src/components/editors/BlockNoteEditorMentionHoverCard.tsx +1 -0
- package/src/components/forms/CommonDeleter.tsx +1 -1
- package/src/components/forms/CommonEditorButtons.tsx +1 -1
- package/src/components/forms/DatePickerPopover.tsx +2 -2
- package/src/components/forms/DateRangeSelector.tsx +2 -2
- package/src/components/forms/EditorSheet.tsx +18 -9
- package/src/components/forms/EntityMultiSelector.tsx +7 -7
- package/src/components/forms/EntitySelector.tsx +8 -8
- package/src/components/forms/FileUploader.tsx +1 -0
- package/src/components/forms/FormCheckbox.tsx +3 -3
- package/src/components/forms/FormDateTime.tsx +3 -3
- package/src/components/forms/FormFieldWrapper.tsx +1 -1
- package/src/components/forms/FormPlaceAutocomplete.tsx +2 -2
- package/src/components/forms/GdprConsentCheckbox.tsx +1 -1
- package/src/components/forms/MultiFileUploader.tsx +1 -1
- package/src/components/forms/PasswordInput.tsx +1 -1
- package/src/components/forms/__tests__/FormCheckbox.test.tsx +4 -4
- package/src/components/navigations/Header.tsx +2 -2
- package/src/components/navigations/PageSection.tsx +1 -1
- package/src/components/pages/PageContainerContentDetails.tsx +1 -1
- package/src/components/pages/PageContentContainer.tsx +5 -2
- package/src/components/tables/ContentListTable.tsx +4 -4
- package/src/components/tables/ContentTableSearch.tsx +5 -5
- package/src/contexts/DirectionContext.tsx +25 -0
- package/src/contexts/__tests__/DirectionContext.test.tsx +22 -0
- package/src/contexts/index.ts +1 -0
- package/src/features/ai-connection/components/lists/AiConnectionTypeCard.tsx +1 -1
- package/src/features/assistant/components/containers/AssistantPageContainer.tsx +1 -1
- package/src/features/assistant/components/parts/AssistantBlockNoteComposer.tsx +2 -2
- package/src/features/assistant/components/parts/AssistantComposer.tsx +2 -2
- package/src/features/assistant/components/parts/AssistantEmptyState.tsx +1 -1
- package/src/features/assistant/components/parts/AssistantSidebar.tsx +3 -3
- package/src/features/assistant-action/components/ApprovalActionCard.tsx +1 -1
- package/src/features/assistant-action/components/AssistantContainerWithApprovals.tsx +1 -1
- package/src/features/assistant-message/components/MessageItem.tsx +3 -3
- package/src/features/assistant-message/components/__tests__/MessageItem.spec.tsx +1 -0
- package/src/features/assistant-message/components/parts/MessageSourcesPanel.tsx +2 -2
- package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +1 -1
- package/src/features/assistant-message/components/parts/tabs/SuggestedQuestionsTab.tsx +1 -1
- package/src/features/auth/components/buttons/GoogleSignInButton.tsx +1 -1
- package/src/features/auth/components/forms/Register.tsx +2 -2
- package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +3 -3
- package/src/features/auth/utils/__tests__/clearClientStorage.spec.ts +4 -4
- package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +1 -1
- package/src/features/billing/components/cards/CustomerInfoCard.tsx +1 -1
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +1 -1
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +1 -1
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +1 -1
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +1 -1
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +1 -1
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +6 -6
- package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +2 -2
- package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +3 -3
- package/src/features/billing/stripe-product/hooks/useStripeProductTableStructure.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +4 -4
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +2 -2
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +2 -2
- package/src/features/company/components/forms/CompanyDeleter.tsx +4 -4
- package/src/features/content/hooks/useContentTableStructure.tsx +1 -1
- package/src/features/feature/components/forms/FormFeatures.tsx +2 -2
- package/src/features/help/components/HelpAssistantSheet.tsx +3 -2
- package/src/features/help/components/HelpHint.tsx +2 -2
- package/src/features/help/components/HelpTOC.tsx +1 -1
- package/src/features/how-to/components/containers/HowToCommand.tsx +2 -2
- package/src/features/how-to/components/containers/HowToCommandViewer.tsx +5 -5
- package/src/features/how-to/components/forms/HowToEditor.tsx +3 -3
- package/src/features/how-to/components/forms/HowToSelector.tsx +5 -5
- package/src/features/notification/components/lists/NotificationsList.tsx +2 -2
- package/src/features/notification/components/modals/NotificationModal.tsx +1 -1
- package/src/features/oauth/components/OAuthClientCard.tsx +2 -2
- package/src/features/oauth/components/OAuthClientDetail.tsx +2 -2
- package/src/features/oauth/components/OAuthClientList.tsx +3 -3
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +1 -1
- package/src/features/oauth/components/OAuthScopeSelector.tsx +2 -2
- package/src/features/onboarding/components/OnboardingCard.tsx +1 -7
- package/src/features/rbac/components/RbacByRoleContainer.tsx +3 -3
- package/src/features/rbac/components/RbacContainer.tsx +3 -3
- package/src/features/referral/components/ReferralWidget.tsx +1 -0
- package/src/features/role/components/forms/FormRoles.tsx +1 -1
- package/src/features/role/components/forms/RemoveUserFromRole.tsx +1 -1
- package/src/features/role/components/forms/UserRoleAdd.tsx +1 -1
- package/src/features/tokenusage/components/TokenUsageBreakdownTable.tsx +9 -9
- package/src/features/tokenusage/components/TokenUsageRankedBar.tsx +8 -3
- package/src/features/tokenusage/components/TokenUsageTimelineChart.tsx +5 -1
- package/src/features/user/components/details/UserStandaloneDetails.tsx +1 -1
- package/src/features/user/components/forms/UserReactivator.tsx +2 -2
- package/src/features/user/components/forms/UserResentInvitationEmail.tsx +1 -1
- package/src/features/user/components/forms/UserSelector.tsx +7 -7
- package/src/features/user/components/lists/ContributorsList.tsx +1 -1
- package/src/features/user/components/lists/UserListInAdd.tsx +1 -1
- package/src/features/user/components/widgets/UserSearchPopover.tsx +5 -5
- package/src/features/user/hooks/useUserTableStructure.tsx +1 -1
- package/src/features/waitlist/components/forms/WaitlistQuestionnaireRenderer.tsx +1 -1
- package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +1 -1
- package/src/hooks/useCustomD3Graph.tsx +16 -0
- package/src/lib/__tests__/direction.test.ts +18 -0
- package/src/lib/direction.ts +8 -0
- package/src/shadcnui/custom/multi-select.tsx +6 -6
- package/src/shadcnui/custom/multiple-selector.tsx +6 -2
- package/src/shadcnui/ui/accordion.tsx +1 -1
- package/src/shadcnui/ui/alert-dialog.tsx +2 -1
- package/src/shadcnui/ui/alert.tsx +2 -2
- package/src/shadcnui/ui/avatar.tsx +1 -1
- package/src/shadcnui/ui/badge.tsx +1 -1
- package/src/shadcnui/ui/breadcrumb.tsx +1 -1
- package/src/shadcnui/ui/button.tsx +4 -4
- package/src/shadcnui/ui/calendar.tsx +9 -8
- package/src/shadcnui/ui/carousel.tsx +18 -11
- package/src/shadcnui/ui/combobox.tsx +3 -3
- package/src/shadcnui/ui/command.tsx +2 -2
- package/src/shadcnui/ui/context-menu.tsx +15 -11
- package/src/shadcnui/ui/dialog.tsx +2 -1
- package/src/shadcnui/ui/drawer.tsx +19 -3
- package/src/shadcnui/ui/dropdown-menu.tsx +12 -10
- package/src/shadcnui/ui/field.tsx +2 -2
- package/src/shadcnui/ui/input-group.tsx +3 -3
- package/src/shadcnui/ui/input-otp.tsx +2 -0
- package/src/shadcnui/ui/navigation-menu.tsx +2 -2
- package/src/shadcnui/ui/progress.tsx +1 -1
- package/src/shadcnui/ui/radio-group.tsx +1 -0
- package/src/shadcnui/ui/resizable.tsx +1 -0
- package/src/shadcnui/ui/scroll-area.tsx +1 -1
- package/src/shadcnui/ui/select.tsx +2 -2
- package/src/shadcnui/ui/sheet.tsx +11 -4
- package/src/shadcnui/ui/sidebar.tsx +37 -20
- package/src/shadcnui/ui/sonner.tsx +6 -1
- package/src/shadcnui/ui/switch.tsx +1 -1
- package/src/shadcnui/ui/table.tsx +2 -2
- package/src/shadcnui/ui/tabs.tsx +1 -1
- package/dist/chunk-5WFYQE22.js.map +0 -1
- package/dist/chunk-CFZW6GU4.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-BYMRWUVG.mjs.map → BlockNoteEditor-3D5D7YL7.mjs.map} +0 -0
|
@@ -90,21 +90,26 @@ export function TokenUsageRankedBar({ rows, metric, emptyLabel, labelsAreOperati
|
|
|
90
90
|
<div key={row.id} className="grid grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-x-3 gap-y-0.5 py-0.5">
|
|
91
91
|
<div className="grid min-w-0 gap-0.5">
|
|
92
92
|
<span className="text-muted-foreground truncate text-xs">{renderLabel(row)}</span>
|
|
93
|
-
|
|
93
|
+
{/* The bar itself is a magnitude drawn on an axis, not prose: it always grows
|
|
94
|
+
rightward and only its trailing corner is rounded, so the track and its fill
|
|
95
|
+
stay an LTR island. The label, value and share around it mirror normally.
|
|
96
|
+
rtl-ok: deliberate LTR island (chart) */}
|
|
97
|
+
<div className="bg-muted h-2 w-full overflow-hidden rounded-r-[4px]" dir="ltr">
|
|
94
98
|
<div
|
|
95
99
|
data-testid={`ranked-fill-${row.id}`}
|
|
96
100
|
data-ramp-step={String(step)}
|
|
101
|
+
/* rtl-ok: inside the LTR island above */
|
|
97
102
|
className="h-full rounded-r-[4px]"
|
|
98
103
|
style={{ width: `${width}%`, backgroundColor: SEQUENTIAL_RAMP[step] }}
|
|
99
104
|
/>
|
|
100
105
|
</div>
|
|
101
106
|
</div>
|
|
102
|
-
<span data-testid={`ranked-value-${row.id}`} className="text-
|
|
107
|
+
<span data-testid={`ranked-value-${row.id}`} className="text-end text-xs tabular-nums">
|
|
103
108
|
{formatValue(value, metric)}
|
|
104
109
|
</span>
|
|
105
110
|
<span
|
|
106
111
|
data-testid={`ranked-share-${row.id}`}
|
|
107
|
-
className="text-muted-foreground w-16 text-
|
|
112
|
+
className="text-muted-foreground w-16 text-end text-xs tabular-nums"
|
|
108
113
|
>
|
|
109
114
|
{percent(share)}
|
|
110
115
|
</span>
|
|
@@ -161,7 +161,11 @@ export function TokenUsageTimelineChart({ rows, metric, stackBy, className }: To
|
|
|
161
161
|
series === OTHER_SERIES ? OTHER_COLOR : seriesColor(index, mode);
|
|
162
162
|
|
|
163
163
|
return (
|
|
164
|
-
|
|
164
|
+
// The chart is a coordinate system, not prose: recharts lays the axes and the
|
|
165
|
+
// stacked bars out left-to-right, so the whole chart stays an LTR island even
|
|
166
|
+
// under dir="rtl". Only the labels inside it are translated.
|
|
167
|
+
// rtl-ok: deliberate LTR island (chart)
|
|
168
|
+
<div className={className} dir="ltr">
|
|
165
169
|
{/* The pivot, exposed for assertions and for screen readers that would
|
|
166
170
|
otherwise get nothing from the SVG. */}
|
|
167
171
|
<span className="sr-only" data-testid="timeline-data">
|
|
@@ -29,7 +29,7 @@ export function UserStanadaloneDetails({ user }: UserDetailsProps) {
|
|
|
29
29
|
<div className="flex flex-wrap gap-2">
|
|
30
30
|
{user.roles.map((role: RoleInterface, _index: number) => (
|
|
31
31
|
<Link key={role.id} href={generateUrl({ page: Modules.Role, id: role.id })}>
|
|
32
|
-
<Badge className="
|
|
32
|
+
<Badge className="me-2" variant={`default`}>
|
|
33
33
|
{t(`role.roles`, { role: role.id.replaceAll(`-`, ``) })}
|
|
34
34
|
</Badge>
|
|
35
35
|
</Link>
|
|
@@ -43,7 +43,7 @@ function UserReactivatorInterface({ user, propagateChanges }: UserReactivatorPro
|
|
|
43
43
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
44
44
|
<DialogTrigger>
|
|
45
45
|
<Button size="sm">
|
|
46
|
-
<UserCheckIcon className="
|
|
46
|
+
<UserCheckIcon className="me-3 h-3.5 w-3.5" />
|
|
47
47
|
{t(`user.buttons.reactivate`)}
|
|
48
48
|
</Button>
|
|
49
49
|
</DialogTrigger>
|
|
@@ -54,7 +54,7 @@ function UserReactivatorInterface({ user, propagateChanges }: UserReactivatorPro
|
|
|
54
54
|
</DialogHeader>
|
|
55
55
|
{t(`user.reactivate.description`, { name: user.name })}
|
|
56
56
|
<div className="flex justify-end">
|
|
57
|
-
<Button className="
|
|
57
|
+
<Button className="me-2" variant={"outline"} type={`button`} onClick={() => setOpen(false)}>
|
|
58
58
|
{t(`ui.buttons.cancel`)}
|
|
59
59
|
</Button>
|
|
60
60
|
<Button
|
|
@@ -63,7 +63,7 @@ function UserResentInvitationEmailInternal({ user }: UserResentInvitationEmailPr
|
|
|
63
63
|
</DialogHeader>
|
|
64
64
|
{t(`user.resend_activation.description`, { email: user.email })}
|
|
65
65
|
<div className="flex justify-end">
|
|
66
|
-
<Button className="
|
|
66
|
+
<Button className="me-2" variant={"outline"} type={`button`} onClick={() => setOpen(false)}>
|
|
67
67
|
{t(`ui.buttons.cancel`)}
|
|
68
68
|
</Button>
|
|
69
69
|
<Button
|
|
@@ -97,7 +97,7 @@ export function UserSelector({ id, form, label, placeholder, onChange, isRequire
|
|
|
97
97
|
{field.value ? (
|
|
98
98
|
<div className="bg-input/20 dark:bg-input/30 border-input flex h-7 w-full flex-row items-center justify-start rounded-md border px-2 py-0.5 text-sm md:text-xs/relaxed">
|
|
99
99
|
<div className="*:ring-border *:ring-1">
|
|
100
|
-
<Avatar className={`
|
|
100
|
+
<Avatar className={`me-2 h-4 w-4`}>
|
|
101
101
|
<AvatarImage src={field.value?.avatar} />
|
|
102
102
|
<AvatarFallback>
|
|
103
103
|
{field.value?.name
|
|
@@ -117,7 +117,7 @@ export function UserSelector({ id, form, label, placeholder, onChange, isRequire
|
|
|
117
117
|
</PopoverTrigger>
|
|
118
118
|
{field.value && (
|
|
119
119
|
<CircleX
|
|
120
|
-
className="text-muted hover:text-destructive
|
|
120
|
+
className="text-muted hover:text-destructive ms-2 h-4 w-4 shrink-0 cursor-pointer"
|
|
121
121
|
onClick={() => setUser()}
|
|
122
122
|
/>
|
|
123
123
|
)}
|
|
@@ -125,19 +125,19 @@ export function UserSelector({ id, form, label, placeholder, onChange, isRequire
|
|
|
125
125
|
<PopoverContent align="start" className="w-(--anchor-width)">
|
|
126
126
|
<Command shouldFilter={false}>
|
|
127
127
|
<div className="relative mb-2 w-full">
|
|
128
|
-
<SearchIcon className="text-muted-foreground absolute top-2.5
|
|
128
|
+
<SearchIcon className="text-muted-foreground absolute top-2.5 start-2.5 h-4 w-4" />
|
|
129
129
|
<Input
|
|
130
130
|
placeholder={t(`ui.search.placeholder`, { type: t(`entities.users`, { count: 1 }) })}
|
|
131
131
|
type="text"
|
|
132
|
-
className="w-full
|
|
132
|
+
className="w-full pe-8 ps-8"
|
|
133
133
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
134
134
|
value={searchTerm}
|
|
135
135
|
/>
|
|
136
136
|
{isSearching ? (
|
|
137
|
-
<RefreshCwIcon className="text-muted-foreground absolute top-2.5
|
|
137
|
+
<RefreshCwIcon className="text-muted-foreground absolute top-2.5 end-2.5 h-4 w-4 animate-spin" />
|
|
138
138
|
) : searchTermRef.current ? (
|
|
139
139
|
<XIcon
|
|
140
|
-
className={`absolute top-2.5
|
|
140
|
+
className={`absolute top-2.5 end-2.5 h-4 w-4 ${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}`}
|
|
141
141
|
onClick={() => {
|
|
142
142
|
setSearchTerm("");
|
|
143
143
|
search("");
|
|
@@ -156,7 +156,7 @@ export function UserSelector({ id, form, label, placeholder, onChange, isRequire
|
|
|
156
156
|
key={user.id}
|
|
157
157
|
onSelect={() => setUser(user)}
|
|
158
158
|
>
|
|
159
|
-
<UserAvatar user={user} className={`
|
|
159
|
+
<UserAvatar user={user} className={`me-2 h-4 w-4`} />
|
|
160
160
|
<span className="">{user.name}</span>
|
|
161
161
|
</CommandItem>
|
|
162
162
|
))}
|
|
@@ -21,7 +21,7 @@ export function ContributorsList({ content }: ContributorsListProps) {
|
|
|
21
21
|
href={generateUrl({ page: Modules.User, id: content.author.id })}
|
|
22
22
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.stopPropagation()}
|
|
23
23
|
>
|
|
24
|
-
<UserAvatar user={content.author} className="
|
|
24
|
+
<UserAvatar user={content.author} className="me-1 h-6 w-6" />
|
|
25
25
|
</Link>
|
|
26
26
|
<div className="flex flex-row-reverse justify-end -space-x-1 space-x-reverse">
|
|
27
27
|
{content.editors
|
|
@@ -39,7 +39,7 @@ export function UserListInAdd({ data, existingUsers, setSelectedUser, setLevelOp
|
|
|
39
39
|
>
|
|
40
40
|
<div className="flex w-full flex-row items-center justify-between px-4 py-1">
|
|
41
41
|
<UserAvatar user={user} />
|
|
42
|
-
<div className="
|
|
42
|
+
<div className="ms-5 flex w-full flex-col">
|
|
43
43
|
<div className="text-sm font-medium">{user.name}</div>
|
|
44
44
|
<div className="text-xs font-normal">{user.email}</div>
|
|
45
45
|
</div>
|
|
@@ -45,20 +45,20 @@ export const UserSearchPopover = ({ children, onSelect, align = "start", classNa
|
|
|
45
45
|
<PopoverContent align={align} onClick={(e) => e.stopPropagation()} className={className ?? "w-80"}>
|
|
46
46
|
<Command shouldFilter={false}>
|
|
47
47
|
<div className="relative mb-2 w-full">
|
|
48
|
-
<SearchIcon className="text-muted-foreground absolute top-2.5
|
|
48
|
+
<SearchIcon className="text-muted-foreground absolute top-2.5 start-2.5 h-4 w-4" />
|
|
49
49
|
<Input
|
|
50
50
|
placeholder={t(`ui.search.placeholder`, { type: t(`entities.users`, { count: 1 }) })}
|
|
51
51
|
type="text"
|
|
52
|
-
className="w-full
|
|
52
|
+
className="w-full pe-8 ps-8"
|
|
53
53
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
54
54
|
value={searchQuery}
|
|
55
55
|
onClick={(e) => e.stopPropagation()}
|
|
56
56
|
/>
|
|
57
57
|
{isLoading ? (
|
|
58
|
-
<RefreshCwIcon className="text-muted-foreground absolute top-2.5
|
|
58
|
+
<RefreshCwIcon className="text-muted-foreground absolute top-2.5 end-2.5 h-4 w-4 animate-spin" />
|
|
59
59
|
) : searchQuery ? (
|
|
60
60
|
<XIcon
|
|
61
|
-
className="text-muted-foreground hover:text-foreground absolute top-2.5
|
|
61
|
+
className="text-muted-foreground hover:text-foreground absolute top-2.5 end-2.5 h-4 w-4 cursor-pointer"
|
|
62
62
|
onClick={(e) => {
|
|
63
63
|
e.stopPropagation();
|
|
64
64
|
clearSearch();
|
|
@@ -74,7 +74,7 @@ export const UserSearchPopover = ({ children, onSelect, align = "start", classNa
|
|
|
74
74
|
className="cursor-pointer hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent"
|
|
75
75
|
onSelect={() => handleSelectUser(user.id)}
|
|
76
76
|
>
|
|
77
|
-
<UserAvatar user={user} className="
|
|
77
|
+
<UserAvatar user={user} className="me-2 h-4 w-4" showLink={false} />
|
|
78
78
|
<span>{user.name}</span>
|
|
79
79
|
</CommandItem>
|
|
80
80
|
))
|
|
@@ -99,7 +99,7 @@ export const useUserTableStructure: UseTableStructureHook<UserInterface, UserFie
|
|
|
99
99
|
return (
|
|
100
100
|
<div className="relative flex h-5 w-20 items-center justify-center overflow-clip rounded border text-center">
|
|
101
101
|
<div
|
|
102
|
-
className={`bg-accent absolute top-0
|
|
102
|
+
className={`bg-accent absolute top-0 start-0 h-full opacity-${Math.round(user.relevance)}`}
|
|
103
103
|
style={{ width: `${user.relevance}%` }}
|
|
104
104
|
></div>
|
|
105
105
|
<span
|
|
@@ -84,7 +84,7 @@ export function WaitlistQuestionnaireRenderer<T extends FieldValues>({
|
|
|
84
84
|
<div key={field.id} className="space-y-2">
|
|
85
85
|
<span className="text-sm font-medium">
|
|
86
86
|
{field.label}
|
|
87
|
-
{field.required && <span className="
|
|
87
|
+
{field.required && <span className="ms-1 text-destructive">*</span>}
|
|
88
88
|
</span>
|
|
89
89
|
{field.description && <p className="text-muted-foreground text-xs">{field.description}</p>}
|
|
90
90
|
<div className="space-y-2">
|
|
@@ -94,7 +94,7 @@ export function useWaitlistTableStructure({
|
|
|
94
94
|
if (entry.status === "confirmed") {
|
|
95
95
|
return (
|
|
96
96
|
<Button size="sm" variant="outline" onClick={() => onInvite(entry)}>
|
|
97
|
-
<Send className="
|
|
97
|
+
<Send className="me-2 h-4 w-4" />
|
|
98
98
|
{t("waitlist.admin.actions.invite")}
|
|
99
99
|
</Button>
|
|
100
100
|
);
|
|
@@ -30,6 +30,11 @@ function estimateLabelWidth(node: D3Node): number {
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Custom hook for D3 graph visualization with larger circles and more interactive features
|
|
33
|
+
*
|
|
34
|
+
* Direction: the hook stamps `dir="ltr"` (and `direction: ltr`) on the SVG it
|
|
35
|
+
* owns — the layout is coordinate-based, so it is a deliberate LTR island and
|
|
36
|
+
* never mirrors under `dir="rtl"`. Consumers do not need to set anything; a
|
|
37
|
+
* wrapping container may still mirror around the graph.
|
|
33
38
|
*/
|
|
34
39
|
export function useCustomD3Graph(
|
|
35
40
|
nodes: D3Node[],
|
|
@@ -199,6 +204,17 @@ export function useCustomD3Graph(
|
|
|
199
204
|
);
|
|
200
205
|
|
|
201
206
|
useEffect(() => {
|
|
207
|
+
// The graph lives in SVG user space: node positions, link endpoints and label
|
|
208
|
+
// offsets are all computed coordinates, so an RTL ancestor must not mirror it.
|
|
209
|
+
// Stamped on the element the hook owns rather than left to each caller, so no
|
|
210
|
+
// consumer can forget it. Both the attribute and the inline property are set
|
|
211
|
+
// because `dir` is an HTML attribute and does not reliably reach SVG.
|
|
212
|
+
// rtl-ok: deliberate LTR island (chart)
|
|
213
|
+
if (svgRef.current) {
|
|
214
|
+
svgRef.current.setAttribute("dir", "ltr");
|
|
215
|
+
svgRef.current.style.direction = "ltr";
|
|
216
|
+
}
|
|
217
|
+
|
|
202
218
|
if (!nodes.length || !svgRef.current) return;
|
|
203
219
|
|
|
204
220
|
const visibleNodes = visibleNodeIds
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { resolvePhysicalSide } from "../direction";
|
|
3
|
+
|
|
4
|
+
describe("resolvePhysicalSide", () => {
|
|
5
|
+
it("maps start/end by direction", () => {
|
|
6
|
+
expect(resolvePhysicalSide("start", "ltr")).toBe("left");
|
|
7
|
+
expect(resolvePhysicalSide("start", "rtl")).toBe("right");
|
|
8
|
+
expect(resolvePhysicalSide("end", "ltr")).toBe("right");
|
|
9
|
+
expect(resolvePhysicalSide("end", "rtl")).toBe("left");
|
|
10
|
+
});
|
|
11
|
+
it("passes physical sides through unchanged in both directions", () => {
|
|
12
|
+
for (const dir of ["ltr", "rtl"] as const) {
|
|
13
|
+
for (const side of ["top", "bottom", "left", "right"] as const) {
|
|
14
|
+
expect(resolvePhysicalSide(side, dir)).toBe(side);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type PhysicalSide = "top" | "bottom" | "left" | "right";
|
|
2
|
+
export type LogicalSide = PhysicalSide | "start" | "end";
|
|
3
|
+
|
|
4
|
+
export function resolvePhysicalSide(side: LogicalSide, dir: "ltr" | "rtl"): PhysicalSide {
|
|
5
|
+
if (side === "start") return dir === "rtl" ? "right" : "left";
|
|
6
|
+
if (side === "end") return dir === "rtl" ? "left" : "right";
|
|
7
|
+
return side;
|
|
8
|
+
}
|
|
@@ -291,11 +291,11 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
|
|
291
291
|
className={cn(isAnimating ? "animate-bounce" : "", multiSelectVariants({ variant }))}
|
|
292
292
|
style={{ animationDuration: `${animation}s` }}
|
|
293
293
|
>
|
|
294
|
-
{IconComponent && <IconComponent className="
|
|
294
|
+
{IconComponent && <IconComponent className="me-2 h-4 w-4" />}
|
|
295
295
|
{option?.selectedLabel ?? option?.label}
|
|
296
296
|
<XCircle
|
|
297
297
|
data-remove-button
|
|
298
|
-
className="
|
|
298
|
+
className="ms-2 h-4 w-4 cursor-pointer"
|
|
299
299
|
onPointerDown={(event) => {
|
|
300
300
|
event.stopPropagation();
|
|
301
301
|
event.preventDefault();
|
|
@@ -321,7 +321,7 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
|
|
321
321
|
{`+ ${selectedValues.length - maxCount} more`}
|
|
322
322
|
<XCircle
|
|
323
323
|
data-remove-button
|
|
324
|
-
className="
|
|
324
|
+
className="ms-2 h-4 w-4 cursor-pointer"
|
|
325
325
|
onPointerDown={(event) => {
|
|
326
326
|
event.stopPropagation();
|
|
327
327
|
event.preventDefault();
|
|
@@ -388,7 +388,7 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
|
|
388
388
|
>
|
|
389
389
|
<div
|
|
390
390
|
className={cn(
|
|
391
|
-
"border-primary
|
|
391
|
+
"border-primary me-2 flex h-4 w-4 items-center justify-center rounded-sm border",
|
|
392
392
|
selectedValues.length === options.length
|
|
393
393
|
? "bg-primary text-primary-foreground"
|
|
394
394
|
: "opacity-50 [&_svg]:invisible",
|
|
@@ -411,14 +411,14 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
|
|
411
411
|
>
|
|
412
412
|
<div
|
|
413
413
|
className={cn(
|
|
414
|
-
"border-primary
|
|
414
|
+
"border-primary me-2 flex h-4 w-4 items-center justify-center rounded-sm border",
|
|
415
415
|
isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible",
|
|
416
416
|
)}
|
|
417
417
|
>
|
|
418
418
|
<CheckIcon className="h-4 w-4" />
|
|
419
419
|
</div>
|
|
420
420
|
{option.icon && (
|
|
421
|
-
<option.icon className={option.iconClassName ?? "text-muted-foreground
|
|
421
|
+
<option.icon className={option.iconClassName ?? "text-muted-foreground me-2 h-4 w-4"} />
|
|
422
422
|
)}
|
|
423
423
|
<span>{option.label}</span>
|
|
424
424
|
</CommandItem>
|
|
@@ -213,6 +213,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
|
|
213
213
|
const [onScrollbar, setOnScrollbar] = React.useState(false);
|
|
214
214
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
215
215
|
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
|
216
|
+
// rtl-ok: viewport-anchored floating position (physical viewport coordinates, not a layout inset)
|
|
216
217
|
const [dropdownPosition, setDropdownPosition] = React.useState({ top: 0, left: 0, width: 0 });
|
|
217
218
|
|
|
218
219
|
const [selected, setSelected] = React.useState<Option[]>(value || []);
|
|
@@ -331,6 +332,8 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
|
|
331
332
|
const rect = containerRef.current.getBoundingClientRect();
|
|
332
333
|
setDropdownPosition({
|
|
333
334
|
top: rect.bottom + window.scrollY + 4,
|
|
335
|
+
// rtl-ok: viewport-anchored floating position — the portal is placed at the
|
|
336
|
+
// container's measured rect and spans its full width, so it matches in RTL too
|
|
334
337
|
left: rect.left + window.scrollX,
|
|
335
338
|
width: rect.width,
|
|
336
339
|
});
|
|
@@ -502,7 +505,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
|
|
502
505
|
<button
|
|
503
506
|
type="button"
|
|
504
507
|
className={cn(
|
|
505
|
-
"
|
|
508
|
+
"ms-1 rounded-full outline-none ring-offset-background focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
506
509
|
(disabled || option.fixed) && "hidden",
|
|
507
510
|
)}
|
|
508
511
|
onKeyDown={(e) => {
|
|
@@ -550,7 +553,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
|
|
550
553
|
"flex-1 self-baseline bg-transparent outline-none placeholder:text-muted-foreground text-foreground caret-foreground",
|
|
551
554
|
{
|
|
552
555
|
"w-full": hidePlaceholderWhenSelected,
|
|
553
|
-
"
|
|
556
|
+
"ms-1": selected.length !== 0,
|
|
554
557
|
},
|
|
555
558
|
inputProps?.className,
|
|
556
559
|
)}
|
|
@@ -653,6 +656,7 @@ const MultipleSelector = React.forwardRef<MultipleSelectorRef, MultipleSelectorP
|
|
|
653
656
|
className="fixed z-50 max-h-80 rounded-md border bg-background text-foreground shadow-md outline-none animate-in"
|
|
654
657
|
style={{
|
|
655
658
|
top: dropdownPosition.top,
|
|
659
|
+
// rtl-ok: viewport-anchored floating position (measured px, not a logical inset)
|
|
656
660
|
left: dropdownPosition.left,
|
|
657
661
|
width: dropdownPosition.width,
|
|
658
662
|
}}
|
|
@@ -31,7 +31,7 @@ function AccordionTrigger({ className, children, ...props }: AccordionPrimitive.
|
|
|
31
31
|
<AccordionPrimitive.Trigger
|
|
32
32
|
data-slot="accordion-trigger"
|
|
33
33
|
className={cn(
|
|
34
|
-
"**:data-[slot=accordion-trigger-icon]:text-muted-foreground gap-6 p-2 text-
|
|
34
|
+
"**:data-[slot=accordion-trigger-icon]:text-muted-foreground gap-6 p-2 text-start text-xs/relaxed font-medium hover:underline **:data-[slot=accordion-trigger-icon]:ms-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50",
|
|
35
35
|
className,
|
|
36
36
|
)}
|
|
37
37
|
{...props}
|
|
@@ -45,6 +45,7 @@ function AlertDialogContent({
|
|
|
45
45
|
data-slot="alert-dialog-content"
|
|
46
46
|
data-size={size}
|
|
47
47
|
className={cn(
|
|
48
|
+
// rtl-ok: symmetric centering (`left-1/2` + `-translate-x-1/2`) — identical in LTR and RTL
|
|
48
49
|
"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 bg-background ring-foreground/10 gap-3 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-64 data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
|
|
49
50
|
className,
|
|
50
51
|
)}
|
|
@@ -59,7 +60,7 @@ function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">)
|
|
|
59
60
|
<div
|
|
60
61
|
data-slot="alert-dialog-header"
|
|
61
62
|
className={cn(
|
|
62
|
-
"grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-
|
|
63
|
+
"grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-start sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
|
|
63
64
|
className,
|
|
64
65
|
)}
|
|
65
66
|
{...props}
|
|
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
4
4
|
import { cn } from "@/lib/utils";
|
|
5
5
|
|
|
6
6
|
const alertVariants = cva(
|
|
7
|
-
"grid gap-0.5 rounded-lg border px-2 py-1.5 text-
|
|
7
|
+
"grid gap-0.5 rounded-lg border px-2 py-1.5 text-start text-xs/relaxed has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pe-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-1.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-3.5 w-full relative group/alert",
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
@@ -47,7 +47,7 @@ function AlertDescription({ className, ...props }: React.ComponentProps<"div">)
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
50
|
-
return <div data-slot="alert-action" className={cn("absolute top-1.5
|
|
50
|
+
return <div data-slot="alert-action" className={cn("absolute top-1.5 end-2", className)} {...props} />;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export { Alert, AlertTitle, AlertDescription, AlertAction };
|
|
@@ -53,7 +53,7 @@ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
|
53
53
|
<span
|
|
54
54
|
data-slot="avatar-badge"
|
|
55
55
|
className={cn(
|
|
56
|
-
"bg-primary text-primary-foreground ring-background absolute
|
|
56
|
+
"bg-primary text-primary-foreground ring-background absolute end-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
|
|
57
57
|
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
58
58
|
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
59
59
|
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
5
5
|
import { cn } from "@/lib/utils";
|
|
6
6
|
|
|
7
7
|
const badgeVariants = cva(
|
|
8
|
-
"h-5 gap-1 rounded-full border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:
|
|
8
|
+
"h-5 gap-1 rounded-full border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&>svg]:size-2.5! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -61,7 +61,7 @@ function BreadcrumbSeparator({ children, className, ...props }: React.ComponentP
|
|
|
61
61
|
className={cn("[&>svg]:size-3.5", className)}
|
|
62
62
|
{...props}
|
|
63
63
|
>
|
|
64
|
-
{children ?? <ChevronRightIcon />}
|
|
64
|
+
{children ?? <ChevronRightIcon className="rtl:rotate-180" />}
|
|
65
65
|
</li>
|
|
66
66
|
);
|
|
67
67
|
}
|
|
@@ -21,10 +21,10 @@ const buttonVariants = cva(
|
|
|
21
21
|
},
|
|
22
22
|
size: {
|
|
23
23
|
default:
|
|
24
|
-
"h-7 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:
|
|
25
|
-
xs: "h-5 gap-1 rounded-sm px-2 text-xs/relaxed has-data-[icon=inline-end]:
|
|
26
|
-
sm: "h-6 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:
|
|
27
|
-
lg: "h-8 gap-1 px-2.5 text-xs/relaxed has-data-[icon=inline-end]:
|
|
24
|
+
"h-7 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
25
|
+
xs: "h-5 gap-1 rounded-sm px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-2.5",
|
|
26
|
+
sm: "h-6 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
27
|
+
lg: "h-8 gap-1 px-2.5 text-xs/relaxed has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-4",
|
|
28
28
|
icon: "size-7 [&_svg:not([class*='size-'])]:size-3.5",
|
|
29
29
|
"icon-xs": "size-5 rounded-sm [&_svg:not([class*='size-'])]:size-2.5",
|
|
30
30
|
"icon-sm": "size-6 [&_svg:not([class*='size-'])]:size-3",
|
|
@@ -6,6 +6,7 @@ import { DayPicker, getDefaultClassNames, type DayButton } from "react-day-picke
|
|
|
6
6
|
import { cn } from "@/lib/utils";
|
|
7
7
|
import { Button, buttonVariants } from "@/components/ui/button";
|
|
8
8
|
import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from "lucide-react";
|
|
9
|
+
import { useDir } from "../../contexts/DirectionContext";
|
|
9
10
|
|
|
10
11
|
function Calendar({
|
|
11
12
|
className,
|
|
@@ -20,14 +21,14 @@ function Calendar({
|
|
|
20
21
|
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
21
22
|
}) {
|
|
22
23
|
const defaultClassNames = getDefaultClassNames();
|
|
24
|
+
const dir = useDir();
|
|
23
25
|
|
|
24
26
|
return (
|
|
25
27
|
<DayPicker
|
|
28
|
+
dir={dir}
|
|
26
29
|
showOutsideDays={showOutsideDays}
|
|
27
30
|
className={cn(
|
|
28
31
|
"p-3 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(6)] bg-background group/calendar [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
29
|
-
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
30
|
-
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
31
32
|
className,
|
|
32
33
|
)}
|
|
33
34
|
captionLayout={captionLayout}
|
|
@@ -80,19 +81,19 @@ function Calendar({
|
|
|
80
81
|
week_number_header: cn("select-none w-(--cell-size)", defaultClassNames.week_number_header),
|
|
81
82
|
week_number: cn("text-xs select-none text-muted-foreground", defaultClassNames.week_number),
|
|
82
83
|
day: cn(
|
|
83
|
-
"relative w-full rounded-(--cell-radius) h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-
|
|
84
|
+
"relative w-full rounded-(--cell-radius) h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius) group/day aspect-square select-none",
|
|
84
85
|
props.showWeekNumber
|
|
85
|
-
? "[&:nth-child(2)[data-selected=true]_button]:rounded-
|
|
86
|
-
: "[&:first-child[data-selected=true]_button]:rounded-
|
|
86
|
+
? "[&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)"
|
|
87
|
+
: "[&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)",
|
|
87
88
|
defaultClassNames.day,
|
|
88
89
|
),
|
|
89
90
|
range_start: cn(
|
|
90
|
-
"rounded-
|
|
91
|
+
"rounded-s-(--cell-radius) bg-muted elative after:bg-muted after:absolute after:inset-y-0 after:w-4 after:end-0 -z-0 isolate",
|
|
91
92
|
defaultClassNames.range_start,
|
|
92
93
|
),
|
|
93
94
|
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
94
95
|
range_end: cn(
|
|
95
|
-
"rounded-
|
|
96
|
+
"rounded-e-(--cell-radius) bg-muted relative after:bg-muted after:absolute after:inset-y-0 after:w-4 after:start-0 -z-0 isolate",
|
|
96
97
|
defaultClassNames.range_end,
|
|
97
98
|
),
|
|
98
99
|
today: cn(
|
|
@@ -154,7 +155,7 @@ function CalendarDayButton({ className, day, modifiers, ...props }: React.Compon
|
|
|
154
155
|
data-range-end={modifiers.range_end}
|
|
155
156
|
data-range-middle={modifiers.range_middle}
|
|
156
157
|
className={cn(
|
|
157
|
-
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-
|
|
158
|
+
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-e-(--cell-radius) data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-s-(--cell-radius) [&>span]:text-xs [&>span]:opacity-70",
|
|
158
159
|
defaultClassNames.day,
|
|
159
160
|
className,
|
|
160
161
|
)}
|