@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carlonicora/nextjs-jsonapi",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Next.js JSON:API client with server/client support and caching",
|
|
5
5
|
"author": "Carlo Nicora",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"build": "rm -rf dist && NODE_OPTIONS='--max-old-space-size=8192' tsup && tsc -p scripts/generate-web-module/tsconfig.json",
|
|
74
74
|
"dev": "NODE_OPTIONS='--max-old-space-size=8192' tsup --watch",
|
|
75
75
|
"clean": "rm -rf dist",
|
|
76
|
-
"lint": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" --fix",
|
|
76
|
+
"lint": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" --fix && node scripts/check-rtl-classes.mjs",
|
|
77
77
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
78
78
|
"prepublishOnly": "pnpm build",
|
|
79
79
|
"prepare": "tsc -p scripts/generate-web-module/tsconfig.json",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Lint-time guard: physical directional Tailwind classes / CSS props are
|
|
2
|
+
// forbidden in src/ — use logical equivalents, or escape with `rtl-ok`.
|
|
3
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const PATTERNS = [
|
|
8
|
+
/(?<![-\w:[])(?:ml|mr|pl|pr)-(?:\[|\d|auto|px)/g,
|
|
9
|
+
/(?<![-\w:[])-?(?:left|right)-(?:\[|\d|px|full)/g,
|
|
10
|
+
/(?<![-\w])text-(?:left|right)(?![-\w])/g,
|
|
11
|
+
/(?<![-\w])rounded-(?:[lr]|tl|tr|bl|br)(?![a-z])/g,
|
|
12
|
+
/(?<![-\w])border-[lr](?![a-z])/g,
|
|
13
|
+
/(?<![-\w])float-(?:left|right)(?![-\w])/g,
|
|
14
|
+
/(?<![-\w])scroll-(?:ml|mr|pl|pr)-/g,
|
|
15
|
+
/\b(?:marginLeft|marginRight|paddingLeft|paddingRight)\s*[:=]/g,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function findViolations(source, file) {
|
|
19
|
+
if (/__tests__|\.(test|spec)\.[tj]sx?$/.test(file)) return [];
|
|
20
|
+
const out = [];
|
|
21
|
+
const lines = source.split("\n");
|
|
22
|
+
lines.forEach((line, i) => {
|
|
23
|
+
if (line.includes("rtl-ok") || (i > 0 && lines[i - 1].includes("rtl-ok"))) return;
|
|
24
|
+
if (line.includes("data-[side=")) return; // rtl-ok by design (resolved physical side)
|
|
25
|
+
for (const p of PATTERNS) {
|
|
26
|
+
for (const m of line.matchAll(p)) out.push({ file, line: i + 1, match: m[0] });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function* walk(dir) {
|
|
33
|
+
for (const name of readdirSync(dir)) {
|
|
34
|
+
const p = join(dir, name);
|
|
35
|
+
if (statSync(p).isDirectory()) yield* walk(p);
|
|
36
|
+
else if (/\.(ts|tsx)$/.test(name)) yield p;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// `process.argv[1]` is not symlink-resolved by node while `import.meta.url` is,
|
|
41
|
+
// so compare real paths — otherwise the CLI silently does nothing.
|
|
42
|
+
function isDirectInvocation() {
|
|
43
|
+
try {
|
|
44
|
+
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isDirectInvocation()) {
|
|
51
|
+
const root = join(fileURLToPath(new URL(".", import.meta.url)), "..", "src");
|
|
52
|
+
const all = [];
|
|
53
|
+
for (const f of walk(root)) all.push(...findViolations(readFileSync(f, "utf8"), f));
|
|
54
|
+
if (all.length) {
|
|
55
|
+
for (const v of all) console.error(`${v.file}:${v.line} ${v.match}`);
|
|
56
|
+
console.error(`\n${all.length} physical directional class(es). Use logical utilities or add an rtl-ok comment.`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
console.log("check-rtl-classes: clean");
|
|
60
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { findViolations } from "./check-rtl-classes.mjs";
|
|
3
|
+
|
|
4
|
+
const hits = (src: string) => findViolations(src, "x.tsx").map((v) => v.match);
|
|
5
|
+
|
|
6
|
+
describe("check-rtl-classes", () => {
|
|
7
|
+
it("flags physical directional utilities", () => {
|
|
8
|
+
expect(hits(`<div className="ml-2 pr-4 text-left rounded-l-md border-r left-0 -right-4" />`)).toHaveLength(7);
|
|
9
|
+
expect(hits(`<li style={{ paddingLeft: "1rem" }} />`)).toHaveLength(1);
|
|
10
|
+
});
|
|
11
|
+
it("ignores logical, symmetric, compound and rtl-variant classes", () => {
|
|
12
|
+
expect(
|
|
13
|
+
hits(
|
|
14
|
+
`<div className="ms-2 pe-4 text-start rounded-s-md border-e start-0 inset-x-2 space-x-2 divide-x translate-x-1/2 slide-in-from-left-10 rounded-lg border-x blur-md rtl:rotate-180 rtl:-translate-x-2 data-[side=left]:left-0" />`,
|
|
15
|
+
),
|
|
16
|
+
).toHaveLength(0);
|
|
17
|
+
});
|
|
18
|
+
it("honours rtl-ok escapes on the same or previous line", () => {
|
|
19
|
+
expect(hits(`{/* rtl-ok */}\n<div className="ml-2" />`)).toHaveLength(0);
|
|
20
|
+
expect(hits(`<div className="ml-2" /> {/* rtl-ok */}`)).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
it("skips test files", () => {
|
|
23
|
+
expect(findViolations(`"ml-2"`, "src/x/__tests__/y.test.tsx")).toHaveLength(0);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -10,7 +10,7 @@ type TableCellAvatarProps = {
|
|
|
10
10
|
|
|
11
11
|
export function TableCellAvatar({ image, name, className }: TableCellAvatarProps) {
|
|
12
12
|
return (
|
|
13
|
-
<Avatar className={cn("
|
|
13
|
+
<Avatar className={cn("me-2 h-10 w-10 shrink-0", className)}>
|
|
14
14
|
{image ? <AvatarImage src={image} alt={name} /> : null}
|
|
15
15
|
<AvatarFallback className="text-xs">{getInitials(name)}</AvatarFallback>
|
|
16
16
|
</Avatar>
|
|
@@ -19,7 +19,7 @@ export function PageContainer({ children, testId, className }: PageContainerProp
|
|
|
19
19
|
<Header leftContent={headerLeftContent} logo={headerLogo} mobileChildren={headerMobileChildren}>
|
|
20
20
|
{headerChildren}
|
|
21
21
|
</Header>
|
|
22
|
-
<main className={cn(`flex w-full flex-1 flex-col gap-y-4 pt-4
|
|
22
|
+
<main className={cn(`flex w-full flex-1 flex-col gap-y-4 pt-4 ps-4 pe-0`, className)}>{children}</main>
|
|
23
23
|
</div>
|
|
24
24
|
);
|
|
25
25
|
}
|
|
@@ -57,7 +57,7 @@ export function ReactMarkdownContainer({
|
|
|
57
57
|
li: ({ children }) => <li className={size === "small" ? "text-xs" : ""}>{children}</li>,
|
|
58
58
|
table: ({ children }) => <table className="w-full table-auto border-collapse border">{children}</table>,
|
|
59
59
|
th: ({ children }) => (
|
|
60
|
-
<th className={`border px-4 py-2 text-
|
|
60
|
+
<th className={`border px-4 py-2 text-start ${size === "small" ? "px-2 py-1 text-xs" : ""}`}>
|
|
61
61
|
{children}
|
|
62
62
|
</th>
|
|
63
63
|
),
|
|
@@ -65,9 +65,25 @@ export function ReactMarkdownContainer({
|
|
|
65
65
|
<td className={`border px-4 py-2 ${size === "small" ? "px-2 py-1 text-xs" : ""}`}>{children}</td>
|
|
66
66
|
),
|
|
67
67
|
tr: ({ children }) => <tr className="even:bg-gray-50">{children}</tr>,
|
|
68
|
-
ul: ({ children }) => <ul className={`list-disc ${size === "small" ? "
|
|
68
|
+
ul: ({ children }) => <ul className={`list-disc ${size === "small" ? "ps-3" : "ps-4"}`}>{children}</ul>,
|
|
69
69
|
ol: ({ children }) => (
|
|
70
|
-
<ol className={`list-decimal ${size === "small" ? "
|
|
70
|
+
<ol className={`list-decimal ${size === "small" ? "ps-3" : "ps-4"}`}>{children}</ol>
|
|
71
|
+
),
|
|
72
|
+
// Code is not prose: identifiers, paths and command lines read
|
|
73
|
+
// left-to-right in every locale, so both the fenced block and the
|
|
74
|
+
// inline span are deliberate LTR islands. `node` is react-markdown's
|
|
75
|
+
// hast node — it must not reach the DOM element.
|
|
76
|
+
// rtl-ok: deliberate LTR island (code)
|
|
77
|
+
pre: ({ children, node: _node, ...props }) => (
|
|
78
|
+
<pre dir="ltr" {...props}>
|
|
79
|
+
{children}
|
|
80
|
+
</pre>
|
|
81
|
+
),
|
|
82
|
+
// rtl-ok: deliberate LTR island (code)
|
|
83
|
+
code: ({ children, node: _node, ...props }) => (
|
|
84
|
+
<code dir="ltr" {...props}>
|
|
85
|
+
{children}
|
|
86
|
+
</code>
|
|
71
87
|
),
|
|
72
88
|
h1: ({ children }) => (
|
|
73
89
|
<h1 className={size === "small" ? "my-1 mt-2 text-sm font-bold" : "my-2 mt-4 text-3xl font-semibold"}>
|
|
@@ -98,7 +114,7 @@ export function ReactMarkdownContainer({
|
|
|
98
114
|
</div>
|
|
99
115
|
|
|
100
116
|
{collapsible && !isExpanded && showExpandButton && (
|
|
101
|
-
<div className="pointer-events-none absolute
|
|
117
|
+
<div className="pointer-events-none absolute end-0 bottom-0 start-0 h-12 bg-gradient-to-t from-white to-transparent" />
|
|
102
118
|
)}
|
|
103
119
|
</div>
|
|
104
120
|
|
|
@@ -96,10 +96,10 @@ type RoundPageContainerProps = {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
// Rail trigger class: override the horizontal TabsTrigger defaults for a
|
|
99
|
-
// vertical,
|
|
99
|
+
// vertical, start-aligned, dark-filled active state. tailwind-merge inside cn()
|
|
100
100
|
// resolves the conflicts with the base classes.
|
|
101
101
|
const railTriggerClass = cn(
|
|
102
|
-
"flex w-full items-center justify-start rounded-md px-3 py-1.5 text-
|
|
102
|
+
"flex w-full items-center justify-start rounded-md px-3 py-1.5 text-start text-sm leading-tight whitespace-normal",
|
|
103
103
|
"text-muted-foreground",
|
|
104
104
|
"hover:bg-muted hover:text-foreground",
|
|
105
105
|
"data-[state=active]:bg-foreground data-[state=active]:text-background",
|
|
@@ -237,7 +237,7 @@ export function RoundPageContainer({
|
|
|
237
237
|
// render 78px tall; below the bar it became a visible gap. The
|
|
238
238
|
// swipe-up gesture region is unaffected either way — it is a gesture
|
|
239
239
|
// area, not painted chrome.
|
|
240
|
-
isMobile ? "gap-1 p-1 pt-0" : "p-2 pt-0
|
|
240
|
+
isMobile ? "gap-1 p-1 pt-0" : "p-2 pt-0 ps-0",
|
|
241
241
|
)}
|
|
242
242
|
>
|
|
243
243
|
{/* `min-h-0 flex-1`, NOT `h-full`: the bar below is an in-flow sibling in
|
|
@@ -280,7 +280,7 @@ export function RoundPageContainer({
|
|
|
280
280
|
// render 78px tall; below the bar it became a visible gap. The
|
|
281
281
|
// swipe-up gesture region is unaffected either way — it is a gesture
|
|
282
282
|
// area, not painted chrome.
|
|
283
|
-
isMobile ? "gap-1 p-1 pt-0" : "p-2 pt-0
|
|
283
|
+
isMobile ? "gap-1 p-1 pt-0" : "p-2 pt-0 ps-0",
|
|
284
284
|
)}
|
|
285
285
|
>
|
|
286
286
|
{/* `min-h-0 flex-1`, NOT `h-full`: MobileNavigationBar below is an in-flow
|
|
@@ -323,7 +323,7 @@ export function RoundPageContainer({
|
|
|
323
323
|
{/* Flush-left section rail — md and up */}
|
|
324
324
|
<aside
|
|
325
325
|
data-testid="round-page-rail"
|
|
326
|
-
className="hidden shrink-0 border-
|
|
326
|
+
className="hidden shrink-0 border-e p-4 md:flex md:w-56 md:flex-col md:overflow-y-auto"
|
|
327
327
|
>
|
|
328
328
|
{/* `w-full` is load-bearing: TabsList's base variant carries
|
|
329
329
|
`w-fit`, which shrinks the rail list to its widest label.
|
|
@@ -506,7 +506,7 @@ export function RoundPageContainer({
|
|
|
506
506
|
{details &&
|
|
507
507
|
(isMobile ? (
|
|
508
508
|
<Sheet open={showDetails} onOpenChange={setShowDetails}>
|
|
509
|
-
<SheetContent side="
|
|
509
|
+
<SheetContent side="end">
|
|
510
510
|
<SheetHeader>
|
|
511
511
|
<SheetTitle>{detailsTitle ?? "Details"}</SheetTitle>
|
|
512
512
|
</SheetHeader>
|
|
@@ -533,7 +533,7 @@ export function RoundPageContainer({
|
|
|
533
533
|
// vertically too. Making this the containing block lets
|
|
534
534
|
// `overflow-hidden` clip them again.
|
|
535
535
|
"relative flex h-full shrink-0 flex-col overflow-hidden transition-all duration-300 ease-in-out",
|
|
536
|
-
showDetails ? "w-96 border-
|
|
536
|
+
showDetails ? "w-96 border-s opacity-100" : "ms-0 w-0 border-s-0 opacity-0",
|
|
537
537
|
)}
|
|
538
538
|
>
|
|
539
539
|
{detailsTitle && (
|
|
@@ -14,7 +14,7 @@ export function AttributeElement({ inline, title, value, className }: AttributeE
|
|
|
14
14
|
return (
|
|
15
15
|
<div className={cn(`flex ${inline === true ? "flex-row" : "flex-col"} my-1 justify-start`, className)}>
|
|
16
16
|
{title && (
|
|
17
|
-
<div className={`${inline === true ? "min-w-48
|
|
17
|
+
<div className={`${inline === true ? "min-w-48 pe-4" : "w-full"} text-xs text-muted-foreground`}>{title}</div>
|
|
18
18
|
)}
|
|
19
19
|
{value && <div className="flex w-full flex-col text-sm">{value}</div>}
|
|
20
20
|
</div>
|
|
@@ -51,7 +51,7 @@ export const diffWordInlineContentSpec = createReactInlineContentSpec(
|
|
|
51
51
|
if (diffType === "buttons") {
|
|
52
52
|
const diffIdList = diffId.split(",");
|
|
53
53
|
return (
|
|
54
|
-
<span className="
|
|
54
|
+
<span className="ms-2 inline-flex items-center gap-1">
|
|
55
55
|
<Button
|
|
56
56
|
size="sm"
|
|
57
57
|
variant="ghost"
|
|
@@ -108,7 +108,7 @@ export const diffWordInlineContentSpec = createReactInlineContentSpec(
|
|
|
108
108
|
</span>
|
|
109
109
|
|
|
110
110
|
{!accepted && !rejected && (diffType === "added" || diffType === "removed") && (
|
|
111
|
-
<span className="absolute -top-8
|
|
111
|
+
<span className="absolute -top-8 start-0 z-10 hidden items-center gap-1 rounded border bg-white p-1 shadow-lg group-hover:flex">
|
|
112
112
|
<Button
|
|
113
113
|
size="sm"
|
|
114
114
|
variant="ghost"
|
|
@@ -163,10 +163,10 @@ export const DatePickerPopover = ({
|
|
|
163
163
|
value={inputValue}
|
|
164
164
|
onChange={(e) => handleInputChange(e.target.value)}
|
|
165
165
|
placeholder={datePlaceholder}
|
|
166
|
-
className="
|
|
166
|
+
className="pe-16"
|
|
167
167
|
onClick={(e) => e.stopPropagation()}
|
|
168
168
|
/>
|
|
169
|
-
<div className="absolute
|
|
169
|
+
<div className="absolute end-1 top-1/2 flex -translate-y-1/2 items-center space-x-1">
|
|
170
170
|
<button
|
|
171
171
|
type="button"
|
|
172
172
|
className="flex h-8 w-8 items-center justify-center rounded-md hover:bg-muted"
|
|
@@ -94,7 +94,7 @@ export function DateRangeSelector({ onDateChange, avoidSettingDates, showPreviou
|
|
|
94
94
|
<Button
|
|
95
95
|
id="date"
|
|
96
96
|
variant={"outline"}
|
|
97
|
-
className={cn("w-[300px] justify-start text-
|
|
97
|
+
className={cn("w-[300px] justify-start text-start font-normal", !date && "text-muted-foreground")}
|
|
98
98
|
>
|
|
99
99
|
<CalendarIcon />
|
|
100
100
|
{date?.from ? (
|
|
@@ -122,7 +122,7 @@ export function DateRangeSelector({ onDateChange, avoidSettingDates, showPreviou
|
|
|
122
122
|
<Button
|
|
123
123
|
id="date"
|
|
124
124
|
variant={"outline"}
|
|
125
|
-
className={cn("w-[300px] justify-start text-
|
|
125
|
+
className={cn("w-[300px] justify-start text-start font-normal", !date && "text-muted-foreground")}
|
|
126
126
|
/>
|
|
127
127
|
}
|
|
128
128
|
>
|
|
@@ -57,12 +57,20 @@ function resolveTriggerIsNativeButton(trigger: ReactNode): boolean {
|
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The sheet opens on the inline end (`side="end"`), which SheetContent resolves
|
|
62
|
+
* to the physical `right` in LTR and `left` in RTL — and its own width classes
|
|
63
|
+
* are keyed to that resolved physical side. Each size therefore has to be
|
|
64
|
+
* declared for BOTH sides, or an RTL sheet would fall back to SheetContent's
|
|
65
|
+
* base `data-[side=left]:sm:max-w-sm`.
|
|
66
|
+
*/
|
|
67
|
+
// rtl-ok: keyed to the resolved physical side, not to a writing direction
|
|
60
68
|
const sizeClasses: Record<EditorSheetSize, string> = {
|
|
61
|
-
sm: "data-[side=right]:sm:max-w-2xl",
|
|
62
|
-
md: "data-[side=right]:sm:max-w-3xl",
|
|
63
|
-
lg: "data-[side=right]:sm:max-w-5xl",
|
|
64
|
-
xl: "data-[side=right]:sm:max-w-7xl",
|
|
65
|
-
"2xl": "data-[side=right]:sm:!max-w-[min(96rem,90vw)]",
|
|
69
|
+
sm: "data-[side=right]:sm:max-w-2xl data-[side=left]:sm:max-w-2xl",
|
|
70
|
+
md: "data-[side=right]:sm:max-w-3xl data-[side=left]:sm:max-w-3xl",
|
|
71
|
+
lg: "data-[side=right]:sm:max-w-5xl data-[side=left]:sm:max-w-5xl",
|
|
72
|
+
xl: "data-[side=right]:sm:max-w-7xl data-[side=left]:sm:max-w-7xl",
|
|
73
|
+
"2xl": "data-[side=right]:sm:!max-w-[min(96rem,90vw)] data-[side=left]:sm:!max-w-[min(96rem,90vw)]",
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
export type EditorSheetProps<T extends FieldValues> = {
|
|
@@ -106,7 +114,7 @@ export type EditorSheetProps<T extends FieldValues> = {
|
|
|
106
114
|
closeWithoutConfirm: (open: boolean) => void;
|
|
107
115
|
}) => ReactNode;
|
|
108
116
|
|
|
109
|
-
/** Rendered on the
|
|
117
|
+
/** Rendered on the inline-end side of the header, next to the title/description
|
|
110
118
|
* block. This sits OUTSIDE the <form> element — interactive elements here must
|
|
111
119
|
* use onClick handlers, never type="submit". */
|
|
112
120
|
actions?: ReactNode;
|
|
@@ -271,11 +279,12 @@ export function EditorSheet<T extends FieldValues>({
|
|
|
271
279
|
)}
|
|
272
280
|
</SheetTrigger>
|
|
273
281
|
))}
|
|
274
|
-
<SheetContent side="
|
|
282
|
+
<SheetContent side="end" className={sizeClasses[size]}>
|
|
275
283
|
<SheetHeader className="border-b px-6 py-4">
|
|
276
284
|
{actions ? (
|
|
277
|
-
//
|
|
278
|
-
|
|
285
|
+
// pe-10 clears the SheetContent close button, which sits at the
|
|
286
|
+
// inline end of the sheet (absolute top-4 end-4).
|
|
287
|
+
<div className="flex items-start justify-between gap-x-4 pe-10">
|
|
279
288
|
<div className="flex min-w-0 flex-col gap-y-1.5">
|
|
280
289
|
<SheetTitle>{headerTitle}</SheetTitle>
|
|
281
290
|
<SheetDescription>{headerDescription}</SheetDescription>
|
|
@@ -229,32 +229,32 @@ export function EntityMultiSelector<T extends { id: string }>({
|
|
|
229
229
|
>
|
|
230
230
|
{selectedValues.length > 0 ? (
|
|
231
231
|
<>
|
|
232
|
-
<span className="text-foreground min-w-0 flex-1 truncate text-
|
|
232
|
+
<span className="text-foreground min-w-0 flex-1 truncate text-start">{triggerSummary}</span>
|
|
233
233
|
<span className="bg-primary/10 text-primary shrink-0 rounded-full px-1.5 py-0.5 text-xs font-medium">
|
|
234
234
|
{selectedValues.length}
|
|
235
235
|
</span>
|
|
236
236
|
</>
|
|
237
237
|
) : (
|
|
238
|
-
<span className="text-muted-foreground flex-1 text-
|
|
238
|
+
<span className="text-muted-foreground flex-1 text-start">{placeholder}</span>
|
|
239
239
|
)}
|
|
240
240
|
<ChevronDownIcon className="text-muted-foreground size-3.5 shrink-0" />
|
|
241
241
|
</div>
|
|
242
242
|
</PopoverTrigger>
|
|
243
243
|
<PopoverContent className="w-(--anchor-width) flex flex-col gap-0 p-0" align="start">
|
|
244
244
|
<div className="relative p-1.5">
|
|
245
|
-
<SearchIcon className="text-muted-foreground pointer-events-none absolute top-1/2
|
|
245
|
+
<SearchIcon className="text-muted-foreground pointer-events-none absolute top-1/2 start-3.5 size-3.5 -translate-y-1/2" />
|
|
246
246
|
<Input
|
|
247
247
|
ref={searchInputRef}
|
|
248
248
|
placeholder={placeholder}
|
|
249
249
|
type="text"
|
|
250
|
-
className="h-8 w-full
|
|
250
|
+
className="h-8 w-full pe-7 ps-7 text-xs"
|
|
251
251
|
value={searchTerm}
|
|
252
252
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
253
253
|
/>
|
|
254
254
|
{searchTerm && (
|
|
255
255
|
<button
|
|
256
256
|
type="button"
|
|
257
|
-
className="text-muted-foreground hover:text-foreground absolute top-1/2
|
|
257
|
+
className="text-muted-foreground hover:text-foreground absolute top-1/2 end-3.5 -translate-y-1/2"
|
|
258
258
|
onClick={() => setSearchTerm("")}
|
|
259
259
|
>
|
|
260
260
|
<XIcon className="size-3.5" />
|
|
@@ -271,7 +271,7 @@ export function EntityMultiSelector<T extends { id: string }>({
|
|
|
271
271
|
<button
|
|
272
272
|
key={option.id}
|
|
273
273
|
type="button"
|
|
274
|
-
className="hover:bg-muted flex w-full items-center gap-2.5 rounded-md px-2.5 py-1.5 text-
|
|
274
|
+
className="hover:bg-muted flex w-full items-center gap-2.5 rounded-md px-2.5 py-1.5 text-start text-xs/relaxed"
|
|
275
275
|
onClick={() => toggleEntity(option)}
|
|
276
276
|
>
|
|
277
277
|
{renderOption && option.entityData ? (
|
|
@@ -313,7 +313,7 @@ export function EntityMultiSelector<T extends { id: string }>({
|
|
|
313
313
|
<Badge
|
|
314
314
|
key={value.id}
|
|
315
315
|
variant="outline"
|
|
316
|
-
className="h-auto gap-1.5 rounded-md px-2.5 py-1
|
|
316
|
+
className="h-auto gap-1.5 rounded-md px-2.5 py-1 pe-1.5 text-xs"
|
|
317
317
|
>
|
|
318
318
|
{stableGetFormValueLabel(value)}
|
|
319
319
|
<button
|
|
@@ -169,7 +169,7 @@ export default function EntitySelector<T extends BaseEntity>({
|
|
|
169
169
|
hasValue(effectiveValue) ? "" : "text-muted-foreground"
|
|
170
170
|
} ${disabled ? "cursor-not-allowed opacity-50" : ""}`}
|
|
171
171
|
>
|
|
172
|
-
<span className="min-w-0 flex-1 truncate text-
|
|
172
|
+
<span className="min-w-0 flex-1 truncate text-start">
|
|
173
173
|
{hasValue(effectiveValue)
|
|
174
174
|
? getSelectedItemDisplay
|
|
175
175
|
? getSelectedItemDisplay(effectiveValue)
|
|
@@ -178,7 +178,7 @@ export default function EntitySelector<T extends BaseEntity>({
|
|
|
178
178
|
</span>
|
|
179
179
|
{hasValue(effectiveValue) && !disabled && (
|
|
180
180
|
<CircleX
|
|
181
|
-
className="text-muted hover:text-destructive
|
|
181
|
+
className="text-muted hover:text-destructive ms-auto h-4 w-4 shrink-0 cursor-pointer"
|
|
182
182
|
onClick={(e) => {
|
|
183
183
|
e.stopPropagation();
|
|
184
184
|
e.preventDefault();
|
|
@@ -191,19 +191,19 @@ export default function EntitySelector<T extends BaseEntity>({
|
|
|
191
191
|
<PopoverContent align="start" className="w-(--anchor-width) pointer-events-auto">
|
|
192
192
|
<Command shouldFilter={false}>
|
|
193
193
|
<div className="relative mb-2 w-full">
|
|
194
|
-
<SearchIcon className="text-muted-foreground absolute top-2.5
|
|
194
|
+
<SearchIcon className="text-muted-foreground absolute top-2.5 start-2.5 h-4 w-4" />
|
|
195
195
|
<Input
|
|
196
196
|
placeholder={placeholder}
|
|
197
197
|
type="text"
|
|
198
|
-
className="w-full
|
|
198
|
+
className="w-full pe-8 ps-8"
|
|
199
199
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
200
200
|
value={searchTerm}
|
|
201
201
|
/>
|
|
202
202
|
{isSearching ? (
|
|
203
|
-
<RefreshCwIcon className="text-muted-foreground absolute top-2.5
|
|
203
|
+
<RefreshCwIcon className="text-muted-foreground absolute top-2.5 end-2.5 h-4 w-4 animate-spin" />
|
|
204
204
|
) : searchTermRef.current ? (
|
|
205
205
|
<XIcon
|
|
206
|
-
className="absolute top-2.5
|
|
206
|
+
className="absolute top-2.5 end-2.5 h-4 w-4 cursor-pointer"
|
|
207
207
|
onClick={() => {
|
|
208
208
|
setSearchTerm("");
|
|
209
209
|
search("");
|
|
@@ -221,9 +221,9 @@ export default function EntitySelector<T extends BaseEntity>({
|
|
|
221
221
|
onSelect={handleCreateNew}
|
|
222
222
|
>
|
|
223
223
|
{isCreating ? (
|
|
224
|
-
<RefreshCwIcon className="
|
|
224
|
+
<RefreshCwIcon className="me-2 h-4 w-4 animate-spin" />
|
|
225
225
|
) : (
|
|
226
|
-
<Plus className="
|
|
226
|
+
<Plus className="me-2 h-4 w-4" />
|
|
227
227
|
)}
|
|
228
228
|
<span>{createNewLabel ? createNewLabel(searchTerm) : searchTerm.trim() || ""}</span>
|
|
229
229
|
</CommandItem>
|
|
@@ -317,6 +317,7 @@ export const FileUploaderItem = forwardRef<HTMLDivElement, { index: number } & R
|
|
|
317
317
|
</div>
|
|
318
318
|
<button
|
|
319
319
|
type="button"
|
|
320
|
+
// rtl-ok: already direction-aware — resolves the physical corner from the component's own `dir` prop
|
|
320
321
|
className={cn("absolute", direction === "rtl" ? "top-1 left-1" : "top-1 right-1")}
|
|
321
322
|
onClick={() => removeFileFromSet(index)}
|
|
322
323
|
>
|
|
@@ -15,7 +15,7 @@ type FormCheckboxProps = {
|
|
|
15
15
|
export function FormCheckbox({ form, id, name, labelBefore, description, isRequired }: FormCheckboxProps) {
|
|
16
16
|
const simpleLabel = () => {
|
|
17
17
|
return (
|
|
18
|
-
<FieldLabel htmlFor={id} className={`font-normal ${labelBefore ? "" : "
|
|
18
|
+
<FieldLabel htmlFor={id} className={`font-normal ${labelBefore ? "" : "ms-3"}`}>
|
|
19
19
|
{name}
|
|
20
20
|
</FieldLabel>
|
|
21
21
|
);
|
|
@@ -38,10 +38,10 @@ export function FormCheckbox({ form, id, name, labelBefore, description, isRequi
|
|
|
38
38
|
{(field) => (
|
|
39
39
|
<div className="flex gap-x-4">
|
|
40
40
|
{labelBefore && label()}
|
|
41
|
-
{labelBefore && isRequired && <span className="
|
|
41
|
+
{labelBefore && isRequired && <span className="ms-1 text-destructive">*</span>}
|
|
42
42
|
<Checkbox id={id} checked={field.value ?? false} onCheckedChange={field.onChange} />
|
|
43
43
|
{!labelBefore && label()}
|
|
44
|
-
{!labelBefore && isRequired && <span className="
|
|
44
|
+
{!labelBefore && isRequired && <span className="ms-1 text-destructive">*</span>}
|
|
45
45
|
</div>
|
|
46
46
|
)}
|
|
47
47
|
</FormFieldWrapper>
|
|
@@ -117,7 +117,7 @@ export function FormDateTime({
|
|
|
117
117
|
render={
|
|
118
118
|
<Button
|
|
119
119
|
variant={"outline"}
|
|
120
|
-
className={cn("w-full
|
|
120
|
+
className={cn("w-full ps-3 text-start font-normal", !field.value && "text-muted-foreground")}
|
|
121
121
|
/>
|
|
122
122
|
}
|
|
123
123
|
>
|
|
@@ -126,11 +126,11 @@ export function FormDateTime({
|
|
|
126
126
|
) : (
|
|
127
127
|
<span>{placeholder ? placeholder : t(`common.pick_date_time`)}</span>
|
|
128
128
|
)}
|
|
129
|
-
<CalendarIcon className="
|
|
129
|
+
<CalendarIcon className="ms-auto h-4 w-4 opacity-50" />
|
|
130
130
|
</PopoverTrigger>
|
|
131
131
|
{field.value && allowEmpty !== false && (
|
|
132
132
|
<CircleXIcon
|
|
133
|
-
className="text-muted hover:text-destructive
|
|
133
|
+
className="text-muted hover:text-destructive ms-2 h-6 w-6 cursor-pointer"
|
|
134
134
|
onClick={() => {
|
|
135
135
|
if (onChange) onChange(undefined);
|
|
136
136
|
form.setValue(id, "");
|
|
@@ -43,7 +43,7 @@ export function FormFieldWrapper<T extends FieldValues>({
|
|
|
43
43
|
{label && (
|
|
44
44
|
<FieldLabel>
|
|
45
45
|
{label}
|
|
46
|
-
{isRequired && <span className="
|
|
46
|
+
{isRequired && <span className="ms-1 text-destructive">*</span>}
|
|
47
47
|
</FieldLabel>
|
|
48
48
|
)}
|
|
49
49
|
{children(field, fieldState)}
|
|
@@ -315,14 +315,14 @@ export function FormPlaceAutocomplete({
|
|
|
315
315
|
|
|
316
316
|
{/* Loading indicator */}
|
|
317
317
|
{isLoading && (
|
|
318
|
-
<div className="absolute
|
|
318
|
+
<div className="absolute end-3 top-1/2 -translate-y-1/2">
|
|
319
319
|
<div className="border-primary h-4 w-4 animate-spin rounded-full border-2 border-t-transparent"></div>
|
|
320
320
|
</div>
|
|
321
321
|
)}
|
|
322
322
|
|
|
323
323
|
{/* Suggestions dropdown */}
|
|
324
324
|
{showSuggestions && suggestions.length > 0 && (
|
|
325
|
-
<div className="bg-background absolute
|
|
325
|
+
<div className="bg-background absolute start-0 end-0 top-full z-50 mt-1 max-h-60 overflow-auto rounded-md border shadow-lg">
|
|
326
326
|
{suggestions.map((suggestion, index) => (
|
|
327
327
|
<div
|
|
328
328
|
key={suggestion.place_id || index}
|
|
@@ -28,7 +28,7 @@ export function GdprConsentCheckbox<T extends FieldValues>({
|
|
|
28
28
|
<div className="space-y-1 leading-none">
|
|
29
29
|
<span className="text-sm font-normal">
|
|
30
30
|
{label}
|
|
31
|
-
{required && <span className="
|
|
31
|
+
{required && <span className="ms-1 text-destructive">*</span>}
|
|
32
32
|
</span>
|
|
33
33
|
{description && <FieldDescription className="text-xs">{description}</FieldDescription>}
|
|
34
34
|
</div>
|
|
@@ -87,7 +87,7 @@ const FilePreviewItem = ({ file, onRemoveClick }: { file: File; onRemoveClick: (
|
|
|
87
87
|
<div className="text-muted-foreground group relative aspect-square h-full overflow-hidden rounded-lg border">
|
|
88
88
|
<button
|
|
89
89
|
onClick={onRemoveClick}
|
|
90
|
-
className="absolute top-1.5
|
|
90
|
+
className="absolute top-1.5 end-1.5 z-20 rounded-full bg-black/60 p-1 text-white opacity-0 transition-all duration-200 ease-in-out group-hover:opacity-100 hover:bg-red-600 focus:outline-none"
|
|
91
91
|
aria-label="Remove file"
|
|
92
92
|
>
|
|
93
93
|
<XIcon className="h-3.5 w-3.5" />
|
|
@@ -20,7 +20,7 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
|
20
20
|
type="button"
|
|
21
21
|
variant="ghost"
|
|
22
22
|
size="sm"
|
|
23
|
-
className="absolute
|
|
23
|
+
className="absolute end-0 top-0 h-full px-3 py-2 hover:bg-transparent"
|
|
24
24
|
onClick={() => setShowPassword((prev) => !prev)}
|
|
25
25
|
disabled={disabled}
|
|
26
26
|
>
|
|
@@ -107,7 +107,7 @@ describe.skip("FormCheckbox", () => {
|
|
|
107
107
|
expect(labelIndex).toBeLessThan(checkboxIndex);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
it("should apply
|
|
110
|
+
it("should apply ms-3 class to label when label is after checkbox", () => {
|
|
111
111
|
render(
|
|
112
112
|
<FormWrapper defaultValues={{ active: false }}>
|
|
113
113
|
{(form) => <FormCheckbox form={form} id="active" name="Active" />}
|
|
@@ -115,10 +115,10 @@ describe.skip("FormCheckbox", () => {
|
|
|
115
115
|
);
|
|
116
116
|
|
|
117
117
|
const label = screen.getByText("Active");
|
|
118
|
-
expect(label).toHaveClass("
|
|
118
|
+
expect(label).toHaveClass("ms-3");
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
it("should not apply
|
|
121
|
+
it("should not apply ms-3 class when labelBefore is true", () => {
|
|
122
122
|
render(
|
|
123
123
|
<FormWrapper defaultValues={{ active: false }}>
|
|
124
124
|
{(form) => <FormCheckbox form={form} id="active" name="Active" labelBefore={true} />}
|
|
@@ -126,7 +126,7 @@ describe.skip("FormCheckbox", () => {
|
|
|
126
126
|
);
|
|
127
127
|
|
|
128
128
|
const label = screen.getByText("Active");
|
|
129
|
-
expect(label).not.toHaveClass("
|
|
129
|
+
expect(label).not.toHaveClass("ms-3");
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
|