chore: UI theme cleanup (#47519)

## Problem

- We still use/maintain the legacy theming even though we moved to
Shadcn
- We have two themes list with similar code

## Solution

- Migrate some components to `cva` and `cn` instead of `styleHandler`
- Remove redundant `themes` in favor of `singleThemes`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Standardized theme selection across the app so theme menus
consistently offer the same light/dark/system options, including
dropdowns, navigation menus, and command menus.
* **Refactor**
* Refreshed styling for multiple shared UI components (menus, tabs, side
panels, loading indicators, icons, and modal dialogs) to improve
consistency while preserving visual behavior.
* **Chores**
* Improved the UI patterns CI “validate exports” check for clearer
failure output when exports change unexpectedly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Gildas Garcia authored and GitHub committed 2026-07-02 17:03:08 +02:00
1 parent bc1ad74cfe
commit c41e2835e3
27 files changed
+344 -1009

No files matched your search

+4 -4
View File
@@ -43,10 +43,10 @@ jobs:
- name: Validate exports
run: |
pnpm --filter ui-patterns gen:exports
if ! git diff --ignore-space-at-eol --exit-code --quiet packages/ui-patterns/package.json then
echo "Detected uncommitted changes after build. Run pnpm --filter ui-patterns gen:exports to fix it."
git diff
exit 1
if ! git diff --ignore-space-at-eol --exit-code --quiet packages/ui-patterns/package.json; then
echo "Detected uncommitted changes after build. Run pnpm --filter ui-patterns gen:exports to fix it.";
git diff;
exit 1;
fi
- name: Run tests
run: pnpm run test:ui-patterns
@@ -3,7 +3,7 @@
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
import SVG from 'react-inlinesvg'
import { RadioGroup, RadioGroupLargeItem, singleThemes, Theme } from 'ui'
import { RadioGroup, RadioGroupLargeItem, singleThemes } from 'ui'
const ThemeSettings = () => {
const [mounted, setMounted] = useState(false)
@@ -33,7 +33,7 @@ const ThemeSettings = () => {
value={theme}
className="flex flex-wrap gap-3"
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<RadioGroupLargeItem key={theme.value} value={theme.value} label={theme.name}>
<SVG src={`${process.env.NEXT_PUBLIC_BASE_PATH}/img/themes/${theme.value}.svg`} />
</RadioGroupLargeItem>
@@ -13,7 +13,6 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
singleThemes,
Theme,
} from 'ui'
const ThemeSwitcherDropdown = () => {
@@ -59,7 +58,7 @@ const ThemeSwitcherDropdown = () => {
value={theme}
onValueChange={(themeValue) => setTheme(themeValue)}
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<DropdownMenuRadioItem key={theme.value} value={theme.value}>
{theme.name}
</DropdownMenuRadioItem>
@@ -17,8 +17,7 @@ import {
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
Theme,
themes,
singleThemes,
} from 'ui'
import MenuIconPicker from './MenuIconPicker'
@@ -106,9 +105,9 @@ const TopNavDropdown = () => {
setTheme(value)
}}
>
{themes
{singleThemes
.filter((x) => x.value === 'light' || x.value === 'dark' || x.value === 'system')
.map((theme: Theme) => (
.map((theme) => (
<DropdownMenuRadioItem key={`topnav-theme-${theme.value}`} value={theme.value}>
{theme.name}
</DropdownMenuRadioItem>
@@ -14,7 +14,6 @@ import {
DropdownMenuTrigger,
RadioGroup,
singleThemes,
Theme,
} from 'ui'
const ThemeSwitcherDropdown = () => {
@@ -75,7 +74,7 @@ const ThemeSwitcherDropdown = () => {
value={theme}
onValueChange={(themeValue) => setTheme(themeValue)}
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<DropdownMenuRadioItem key={theme.value} value={theme.value}>
{theme.name}
</DropdownMenuRadioItem>
@@ -15,7 +15,6 @@ import {
SelectValue,
Separator,
singleThemes,
Theme,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import {
@@ -58,7 +57,7 @@ export const ThemeSettings = () => {
value={theme}
className="grid grid-cols-2 gap-4"
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<RadioGroupLargeItem
className="p-3 w-full"
key={theme.value}
@@ -14,7 +14,6 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
singleThemes,
Theme,
} from 'ui'
import { ButtonTooltip } from '../ui/ButtonTooltip'
@@ -83,7 +82,7 @@ export const LocalDropdown = ({
setTheme(value)
}}
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<DropdownMenuRadioItem
key={theme.value}
value={theme.value}
@@ -15,7 +15,6 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
singleThemes,
Theme,
} from 'ui'
import { ButtonTooltip } from '../ui/ButtonTooltip'
@@ -150,7 +149,7 @@ export function UserDropdown({
setTheme(value)
}}
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<DropdownMenuRadioItem
key={theme.value}
value={theme.value}
@@ -14,7 +14,6 @@ import {
DropdownMenuTrigger,
RadioGroup,
singleThemes,
Theme,
} from 'ui'
const ThemeSwitcherDropdown = () => {
@@ -75,7 +74,7 @@ const ThemeSwitcherDropdown = () => {
value={theme}
onValueChange={(themeValue) => setTheme(themeValue)}
>
{singleThemes.map((theme: Theme) => (
{singleThemes.map((theme) => (
<DropdownMenuRadioItem key={theme.value} value={theme.value}>
{theme.name}
</DropdownMenuRadioItem>
+28 -10
View File
@@ -1,6 +1,7 @@
import { cva } from 'class-variance-authority'
import { Dialog } from 'radix-ui'
import { useEffect, useState } from 'react'
import styleHandler from 'ui/src/lib/theme/styleHandler'
import { cn } from 'ui'
export type ImageModalProps = RadixProps & Props
@@ -24,6 +25,28 @@ interface Props {
className?: string
}
const dialogContentVariants = cva(
cn('relative data-open:animate-overlay-show data-closed:animate-overlay-hide'),
{
variants: {
size: {
tiny: `sm:align-middle sm:w-full sm:max-w-xs`,
small: `sm:align-middle sm:w-full sm:max-w-sm`,
medium: `sm:align-middle sm:w-full sm:max-w-lg`,
large: `sm:align-middle sm:w-full md:max-w-xl`,
xlarge: `sm:align-middle sm:w-full md:max-w-3xl`,
xxlarge: `sm:align-middle sm:w-full max-w-screen md:max-w-6xl`,
xxxlarge: `sm:align-middle sm:w-full md:max-w-7xl`,
},
},
}
)
const overlayClasses =
'z-40 fixed bg-alternative h-full w-full left-0 top-0 opacity-75 data-closed:animate-fade-out-overlay-bg data-open:animate-fade-in-overlay-bg'
const scrollOverlayClasses =
'z-40 fixed inset-0 grid place-items-center overflow-y-auto data-open:animate-overlay-show data-closed:animate-overlay-hide'
/**
* Similar to ui/Modal component but with an unstyled dialog content component.
* Mainly used to show images in a overlay.
@@ -37,8 +60,6 @@ const ImageModal = ({
}: ImageModalProps) => {
const [open, setOpen] = useState(visible ? visible : false)
const __styles = styleHandler('modal')
useEffect(() => {
setOpen(visible)
}, [visible])
@@ -53,14 +74,12 @@ const ImageModal = ({
}
}
const contentClasses = 'relative data-open:animate-overlay-show data-closed:animate-overlay-hide'
return (
<Dialog.Root open={open} onOpenChange={handleOpenChange}>
<Dialog.Portal>
<Dialog.Overlay className={__styles.overlay} />
<Dialog.Overlay className={__styles.scroll_overlay}>
<Dialog.Content className={[contentClasses, __styles.size[size], className].join(' ')}>
<Dialog.Overlay className={overlayClasses} />
<Dialog.Overlay className={scrollOverlayClasses}>
<Dialog.Content className={dialogContentVariants({ size, className })}>
{children}
</Dialog.Content>
</Dialog.Overlay>
@@ -70,8 +89,7 @@ const ImageModal = ({
}
function Content({ children }: { children: React.ReactNode }) {
const __styles = styleHandler('modal')
return <div className={__styles.content}>{children}</div>
return <div className="px-5">{children}</div>
}
ImageModal.Content = Content
+61 -117
View File
@@ -86,6 +86,10 @@
"import": "./src/CommandMenu/api/CommandHeader.tsx",
"types": "./src/CommandMenu/api/CommandHeader.tsx"
},
"./CommandMenu/api/CommandMenu": {
"import": "./src/CommandMenu/api/CommandMenu.tsx",
"types": "./src/CommandMenu/api/CommandMenu.tsx"
},
"./CommandMenu/api/CommandMenuInput": {
"import": "./src/CommandMenu/api/CommandMenuInput.tsx",
"types": "./src/CommandMenu/api/CommandMenuInput.tsx"
@@ -94,10 +98,6 @@
"import": "./src/CommandMenu/api/CommandMenuList.tsx",
"types": "./src/CommandMenu/api/CommandMenuList.tsx"
},
"./CommandMenu/api/CommandMenu": {
"import": "./src/CommandMenu/api/CommandMenu.tsx",
"types": "./src/CommandMenu/api/CommandMenu.tsx"
},
"./CommandMenu/api/CommandProvider": {
"import": "./src/CommandMenu/api/CommandProvider.tsx",
"types": "./src/CommandMenu/api/CommandProvider.tsx"
@@ -106,18 +106,10 @@
"import": "./src/CommandMenu/api/TextHighlighter.tsx",
"types": "./src/CommandMenu/api/TextHighlighter.tsx"
},
"./CommandMenu/api/hooks/commandsHooks.test": {
"import": "./src/CommandMenu/api/hooks/commandsHooks.test.tsx",
"types": "./src/CommandMenu/api/hooks/commandsHooks.test.tsx"
},
"./CommandMenu/api/hooks/commandsHooks": {
"import": "./src/CommandMenu/api/hooks/commandsHooks.ts",
"types": "./src/CommandMenu/api/hooks/commandsHooks.ts"
},
"./CommandMenu/api/hooks/pagesHooks.test": {
"import": "./src/CommandMenu/api/hooks/pagesHooks.test.tsx",
"types": "./src/CommandMenu/api/hooks/pagesHooks.test.tsx"
},
"./CommandMenu/api/hooks/pagesHooks": {
"import": "./src/CommandMenu/api/hooks/pagesHooks.ts",
"types": "./src/CommandMenu/api/hooks/pagesHooks.ts"
@@ -162,10 +154,6 @@
"import": "./src/CommandMenu/index.tsx",
"types": "./src/CommandMenu/index.tsx"
},
"./CommandMenu/internal/CommandMenuItem": {
"import": "./src/CommandMenu/internal/CommandMenuItem.tsx",
"types": "./src/CommandMenu/internal/CommandMenuItem.tsx"
},
"./CommandMenu/internal/CommandMenuEmpty": {
"import": "./src/CommandMenu/internal/CommandMenuEmpty.tsx",
"types": "./src/CommandMenu/internal/CommandMenuEmpty.tsx"
@@ -174,6 +162,10 @@
"import": "./src/CommandMenu/internal/CommandMenuGroup.tsx",
"types": "./src/CommandMenu/internal/CommandMenuGroup.tsx"
},
"./CommandMenu/internal/CommandMenuItem": {
"import": "./src/CommandMenu/internal/CommandMenuItem.tsx",
"types": "./src/CommandMenu/internal/CommandMenuItem.tsx"
},
"./CommandMenu/internal/CommandSection": {
"import": "./src/CommandMenu/internal/CommandSection.tsx",
"types": "./src/CommandMenu/internal/CommandSection.tsx"
@@ -186,14 +178,6 @@
"import": "./src/CommandMenu/internal/state/commandsState.ts",
"types": "./src/CommandMenu/internal/state/commandsState.ts"
},
"./CommandMenu/internal/state/commandsStates.test": {
"import": "./src/CommandMenu/internal/state/commandsStates.test.ts",
"types": "./src/CommandMenu/internal/state/commandsStates.test.ts"
},
"./CommandMenu/internal/state/pagesState.test": {
"import": "./src/CommandMenu/internal/state/pagesState.test.ts",
"types": "./src/CommandMenu/internal/state/pagesState.test.ts"
},
"./CommandMenu/internal/state/pagesState": {
"import": "./src/CommandMenu/internal/state/pagesState.ts",
"types": "./src/CommandMenu/internal/state/pagesState.ts"
@@ -238,10 +222,6 @@
"import": "./src/CommandMenu/prepackaged/Support.tsx",
"types": "./src/CommandMenu/prepackaged/Support.tsx"
},
"./CommandMenu/prepackaged/ThemeSwitcher.test": {
"import": "./src/CommandMenu/prepackaged/ThemeSwitcher.test.tsx",
"types": "./src/CommandMenu/prepackaged/ThemeSwitcher.test.tsx"
},
"./CommandMenu/prepackaged/ThemeSwitcher": {
"import": "./src/CommandMenu/prepackaged/ThemeSwitcher.tsx",
"types": "./src/CommandMenu/prepackaged/ThemeSwitcher.tsx"
@@ -258,10 +238,6 @@
"import": "./src/CommandMenu/prepackaged/ai/queryAi.ts",
"types": "./src/CommandMenu/prepackaged/ai/queryAi.ts"
},
"./CommandMenu/prepackaged/ai/useAiChat.test": {
"import": "./src/CommandMenu/prepackaged/ai/useAiChat.test.ts",
"types": "./src/CommandMenu/prepackaged/ai/useAiChat.test.ts"
},
"./CommandMenu/prepackaged/ai/useAiChat": {
"import": "./src/CommandMenu/prepackaged/ai/useAiChat.ts",
"types": "./src/CommandMenu/prepackaged/ai/useAiChat.ts"
@@ -310,6 +286,10 @@
"import": "./src/DataInputs/Input.tsx",
"types": "./src/DataInputs/Input.tsx"
},
"./DatePicker/DatePicker": {
"import": "./src/DatePicker/DatePicker.tsx",
"types": "./src/DatePicker/DatePicker.tsx"
},
"./DatePicker": {
"import": "./src/DatePicker/index.ts",
"types": "./src/DatePicker/index.ts"
@@ -350,10 +330,6 @@
"import": "./src/FilterBar/DefaultCommandList.tsx",
"types": "./src/FilterBar/DefaultCommandList.tsx"
},
"./FilterBar/FilterBar.test": {
"import": "./src/FilterBar/FilterBar.test.tsx",
"types": "./src/FilterBar/FilterBar.test.tsx"
},
"./FilterBar/FilterBar": {
"import": "./src/FilterBar/FilterBar.tsx",
"types": "./src/FilterBar/FilterBar.tsx"
@@ -374,10 +350,6 @@
"import": "./src/FilterBar/OperatorSymbolBadge.tsx",
"types": "./src/FilterBar/OperatorSymbolBadge.tsx"
},
"./FilterBar/hooks.test": {
"import": "./src/FilterBar/hooks.test.ts",
"types": "./src/FilterBar/hooks.test.ts"
},
"./FilterBar/hooks": {
"import": "./src/FilterBar/hooks.ts",
"types": "./src/FilterBar/hooks.ts"
@@ -386,10 +358,6 @@
"import": "./src/FilterBar/index.ts",
"types": "./src/FilterBar/index.ts"
},
"./FilterBar/menuItems.test": {
"import": "./src/FilterBar/menuItems.test.ts",
"types": "./src/FilterBar/menuItems.test.ts"
},
"./FilterBar/menuItems": {
"import": "./src/FilterBar/menuItems.ts",
"types": "./src/FilterBar/menuItems.ts"
@@ -414,10 +382,6 @@
"import": "./src/FilterBar/useKeyboardNavigation.ts",
"types": "./src/FilterBar/useKeyboardNavigation.ts"
},
"./FilterBar/utils.test": {
"import": "./src/FilterBar/utils.test.ts",
"types": "./src/FilterBar/utils.test.ts"
},
"./FilterBar/utils": {
"import": "./src/FilterBar/utils.ts",
"types": "./src/FilterBar/utils.ts"
@@ -450,17 +414,29 @@
"import": "./src/LogsBarChart/index.tsx",
"types": "./src/LogsBarChart/index.tsx"
},
"./Markdown/components": {
"import": "./src/Markdown/components.tsx",
"types": "./src/Markdown/components.tsx"
},
"./Markdown": {
"import": "./src/Markdown/index.tsx",
"types": "./src/Markdown/index.tsx"
},
"./McpUrlBuilder/McpConfigPanel.md": {
"import": "./src/McpUrlBuilder/McpConfigPanel.md.tsx",
"types": "./src/McpUrlBuilder/McpConfigPanel.md.tsx"
},
"./McpUrlBuilder/McpConfigPanel": {
"import": "./src/McpUrlBuilder/McpConfigPanel.tsx",
"types": "./src/McpUrlBuilder/McpConfigPanel.tsx"
},
"./McpUrlBuilder/McpConfigPanel.md": {
"import": "./src/McpUrlBuilder/McpConfigPanel.md.tsx",
"types": "./src/McpUrlBuilder/McpConfigPanel.md.tsx"
"./McpUrlBuilder/clients.data": {
"import": "./src/McpUrlBuilder/clients.data.ts",
"types": "./src/McpUrlBuilder/clients.data.ts"
},
"./McpUrlBuilder/clients.instructions.md": {
"import": "./src/McpUrlBuilder/clients.instructions.md.tsx",
"types": "./src/McpUrlBuilder/clients.instructions.md.tsx"
},
"./McpUrlBuilder/components/ClientSelectDropdown": {
"import": "./src/McpUrlBuilder/components/ClientSelectDropdown.tsx",
@@ -470,6 +446,10 @@
"import": "./src/McpUrlBuilder/components/ConnectionIcon.tsx",
"types": "./src/McpUrlBuilder/components/ConnectionIcon.tsx"
},
"./McpUrlBuilder/components/InstructionBlocks": {
"import": "./src/McpUrlBuilder/components/InstructionBlocks.tsx",
"types": "./src/McpUrlBuilder/components/InstructionBlocks.tsx"
},
"./McpUrlBuilder/components/McpConfigurationDisplay": {
"import": "./src/McpUrlBuilder/components/McpConfigurationDisplay.tsx",
"types": "./src/McpUrlBuilder/components/McpConfigurationDisplay.tsx"
@@ -478,22 +458,14 @@
"import": "./src/McpUrlBuilder/components/McpConfigurationOptions.tsx",
"types": "./src/McpUrlBuilder/components/McpConfigurationOptions.tsx"
},
"./McpUrlBuilder/clients.data": {
"import": "./src/McpUrlBuilder/clients.data.ts",
"types": "./src/McpUrlBuilder/clients.data.ts"
},
"./McpUrlBuilder/utils/serializeMcpConfig": {
"import": "./src/McpUrlBuilder/utils/serializeMcpConfig.ts",
"types": "./src/McpUrlBuilder/utils/serializeMcpConfig.ts"
"./McpUrlBuilder": {
"import": "./src/McpUrlBuilder/index.ts",
"types": "./src/McpUrlBuilder/index.ts"
},
"./McpUrlBuilder/mcpClients": {
"import": "./src/McpUrlBuilder/mcpClients.tsx",
"types": "./src/McpUrlBuilder/mcpClients.tsx"
},
"./McpUrlBuilder": {
"import": "./src/McpUrlBuilder/index.ts",
"types": "./src/McpUrlBuilder/index.ts"
},
"./McpUrlBuilder/types": {
"import": "./src/McpUrlBuilder/types.ts",
"types": "./src/McpUrlBuilder/types.ts"
@@ -506,10 +478,6 @@
"import": "./src/McpUrlBuilder/utils/getMcpButtonData.ts",
"types": "./src/McpUrlBuilder/utils/getMcpButtonData.ts"
},
"./McpUrlBuilder/utils/getMcpIconSrc.test": {
"import": "./src/McpUrlBuilder/utils/getMcpIconSrc.test.ts",
"types": "./src/McpUrlBuilder/utils/getMcpIconSrc.test.ts"
},
"./McpUrlBuilder/utils/getMcpIconSrc": {
"import": "./src/McpUrlBuilder/utils/getMcpIconSrc.ts",
"types": "./src/McpUrlBuilder/utils/getMcpIconSrc.ts"
@@ -522,6 +490,10 @@
"import": "./src/McpUrlBuilder/utils/mcpIconAssets.ts",
"types": "./src/McpUrlBuilder/utils/mcpIconAssets.ts"
},
"./McpUrlBuilder/utils/serializeMcpConfig": {
"import": "./src/McpUrlBuilder/utils/serializeMcpConfig.ts",
"types": "./src/McpUrlBuilder/utils/serializeMcpConfig.ts"
},
"./Mermaid/Mermaid": {
"import": "./src/Mermaid/Mermaid.tsx",
"types": "./src/Mermaid/Mermaid.tsx"
@@ -534,10 +506,6 @@
"import": "./src/MetricCard/index.tsx",
"types": "./src/MetricCard/index.tsx"
},
"./MobileSheetNav/MobileSheetNav.test": {
"import": "./src/MobileSheetNav/MobileSheetNav.test.tsx",
"types": "./src/MobileSheetNav/MobileSheetNav.test.tsx"
},
"./MobileSheetNav/MobileSheetNav": {
"import": "./src/MobileSheetNav/MobileSheetNav.tsx",
"types": "./src/MobileSheetNav/MobileSheetNav.tsx"
@@ -546,26 +514,14 @@
"import": "./src/MobileSheetNav/index.ts",
"types": "./src/MobileSheetNav/index.ts"
},
"./MultiSelectDeprecated/Badges": {
"import": "./src/MultiSelectDeprecated/Badges.tsx",
"types": "./src/MultiSelectDeprecated/Badges.tsx"
},
"./MultiSelectDeprecated/MultiSelectV2": {
"import": "./src/MultiSelectDeprecated/MultiSelectV2.tsx",
"types": "./src/MultiSelectDeprecated/MultiSelectV2.tsx"
},
"./MultiSelectDeprecated": {
"import": "./src/MultiSelectDeprecated/index.tsx",
"types": "./src/MultiSelectDeprecated/index.tsx"
},
"./MultipleCodeBlock/index.test": {
"import": "./src/MultipleCodeBlock/index.test.tsx",
"types": "./src/MultipleCodeBlock/index.test.tsx"
},
"./MultipleCodeBlock": {
"import": "./src/MultipleCodeBlock/index.tsx",
"types": "./src/MultipleCodeBlock/index.tsx"
},
"./PageBreadcrumbs": {
"import": "./src/PageBreadcrumbs/index.tsx",
"types": "./src/PageBreadcrumbs/index.tsx"
},
"./PageContainer": {
"import": "./src/PageContainer/index.tsx",
"types": "./src/PageContainer/index.tsx"
@@ -574,10 +530,6 @@
"import": "./src/PageHeader/index.tsx",
"types": "./src/PageHeader/index.tsx"
},
"./PageBreadcrumbs": {
"import": "./src/PageBreadcrumbs/index.tsx",
"types": "./src/PageBreadcrumbs/index.tsx"
},
"./PageNav": {
"import": "./src/PageNav/index.tsx",
"types": "./src/PageNav/index.tsx"
@@ -614,10 +566,6 @@
"import": "./src/Row/Row.utils.ts",
"types": "./src/Row/Row.utils.ts"
},
"./Row/index.test": {
"import": "./src/Row/index.test.ts",
"types": "./src/Row/index.test.ts"
},
"./Row": {
"import": "./src/Row/index.tsx",
"types": "./src/Row/index.tsx"
@@ -678,6 +626,14 @@
"import": "./src/StatusCode/index.tsx",
"types": "./src/StatusCode/index.tsx"
},
"./Table/TanStackTableHeadSort": {
"import": "./src/Table/TanStackTableHeadSort.tsx",
"types": "./src/Table/TanStackTableHeadSort.tsx"
},
"./Table": {
"import": "./src/Table/index.ts",
"types": "./src/Table/index.ts"
},
"./TextLink": {
"import": "./src/TextLink/index.tsx",
"types": "./src/TextLink/index.tsx"
@@ -686,14 +642,14 @@
"import": "./src/ThemeToggle.tsx",
"types": "./src/ThemeToggle.tsx"
},
"./TimestampInfo/TimestampInfoProvider": {
"import": "./src/TimestampInfo/TimestampInfoProvider.tsx",
"types": "./src/TimestampInfo/TimestampInfoProvider.tsx"
},
"./TimestampInfo": {
"import": "./src/TimestampInfo/index.tsx",
"types": "./src/TimestampInfo/index.tsx"
},
"./Table": {
"import": "./src/Table/index.ts",
"types": "./src/Table/index.ts"
},
"./Toc": {
"import": "./src/Toc/index.ts",
"types": "./src/Toc/index.ts"
@@ -734,14 +690,14 @@
"import": "./src/admonition.tsx",
"types": "./src/admonition.tsx"
},
"./consent": {
"import": "./src/consent.tsx",
"types": "./src/consent.tsx"
},
"./collapsible-alert": {
"import": "./src/collapsible-alert.tsx",
"types": "./src/collapsible-alert.tsx"
},
"./consent": {
"import": "./src/consent.tsx",
"types": "./src/consent.tsx"
},
"./form/FormItemLayout/FormItemLayout": {
"import": "./src/form/FormItemLayout/FormItemLayout.tsx",
"types": "./src/form/FormItemLayout/FormItemLayout.tsx"
@@ -750,10 +706,6 @@
"import": "./src/form/FormLayout2.tsx",
"types": "./src/form/FormLayout2.tsx"
},
"./form/KeyValueFieldArray/KeyValueFieldArray.test": {
"import": "./src/form/KeyValueFieldArray/KeyValueFieldArray.test.tsx",
"types": "./src/form/KeyValueFieldArray/KeyValueFieldArray.test.tsx"
},
"./form/KeyValueFieldArray/KeyValueFieldArray": {
"import": "./src/form/KeyValueFieldArray/KeyValueFieldArray.tsx",
"types": "./src/form/KeyValueFieldArray/KeyValueFieldArray.tsx"
@@ -762,18 +714,14 @@
"import": "./src/form/KeyValueFieldArray/validation.ts",
"types": "./src/form/KeyValueFieldArray/validation.ts"
},
"./form/SingleValueFieldArray/SingleValueFieldArray.test": {
"import": "./src/form/SingleValueFieldArray/SingleValueFieldArray.test.tsx",
"types": "./src/form/SingleValueFieldArray/SingleValueFieldArray.test.tsx"
"./form/Layout/FormLayout": {
"import": "./src/form/Layout/FormLayout.tsx",
"types": "./src/form/Layout/FormLayout.tsx"
},
"./form/SingleValueFieldArray/SingleValueFieldArray": {
"import": "./src/form/SingleValueFieldArray/SingleValueFieldArray.tsx",
"types": "./src/form/SingleValueFieldArray/SingleValueFieldArray.tsx"
},
"./form/Layout/FormLayout": {
"import": "./src/form/Layout/FormLayout.tsx",
"types": "./src/form/Layout/FormLayout.tsx"
},
"./form/utils": {
"import": "./src/form/utils.tsx",
"types": "./src/form/utils.tsx"
@@ -786,10 +734,6 @@
"import": "./src/multi-select/index.tsx",
"types": "./src/multi-select/index.tsx"
},
"./multi-select/multi-select.test": {
"import": "./src/multi-select/multi-select.test.tsx",
"types": "./src/multi-select/multi-select.test.tsx"
},
"./multi-select/multi-select": {
"import": "./src/multi-select/multi-select.tsx",
"types": "./src/multi-select/multi-select.tsx"
@@ -1,4 +1,5 @@
import * as fs from 'node:fs'
import { EOL } from 'node:os'
import * as path from 'node:path'
const SRC_DIR = path.resolve(__dirname, '..', 'src')
@@ -19,7 +20,7 @@ function getAllSourceFiles(dir: string): ExportMap {
if (entry.isDirectory()) {
Object.assign(exportsMap, getAllSourceFiles(fullPath))
} else if (entry.isFile() && /\.(ts|tsx|css)$/.test(entry.name)) {
} else if (entry.isFile() && /\.(ts|tsx|css)$/.test(entry.name) && !/\.test/.test(entry.name)) {
const relativePath = path.relative(SRC_DIR, fullPath)
const noExtension = relativePath.replace(/\.(ts|tsx)$/, '')
const segments = noExtension.split(path.sep)
@@ -48,14 +49,11 @@ function updatePackageJson(exportsMap: ExportMap): void {
packageJson.exports = {
'./package.json': './package.json',
'.': {
import: './index.tsx',
types: './index.tsx',
},
...exportsMap,
}
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
fs.appendFileSync(packageJsonPath, EOL, 'utf8')
console.log('✅ package.json exports updated (with clean index paths).')
}
@@ -19,9 +19,8 @@ import {
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
singleThemes,
} from 'ui'
import { themes } from 'ui/src/components/ThemeProvider/themes'
import type { Theme } from 'ui/src/components/ThemeProvider/themes'
interface Props {
menu: menuItem[][]
@@ -116,11 +115,11 @@ export const AuthenticatedDropdownMenu = ({ user, menu, site }: Props) => {
setTheme(value)
}}
>
{themes
{singleThemes
.filter(
(x) => x.value === 'light' || x.value === 'dark' || x.value === 'system'
)
.map((theme: Theme) => (
.map((theme) => (
<DropdownMenuRadioItem
key={`${site}-auth-dropdown-theme-${theme.value}`}
value={theme.value}
@@ -8,7 +8,7 @@ import {
} from '..'
import { Monitor, MonitorDot, Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'
import { themes } from 'ui/src/components/ThemeProvider/themes'
import { singleThemes } from 'ui'
const THEME_SWITCHER_PAGE_NAME = 'Switch theme'
@@ -29,7 +29,7 @@ const useThemeSwitcherCommands = ({ options }: { options?: CommandOptions } = {}
{
id: 'switch-theme',
name: 'Switch theme',
commands: themes
commands: singleThemes
.filter(({ name }) => name === 'System' || name === 'Light' || name === 'Dark')
.map((theme) => ({
id: `switch-theme-${theme.value}`,
+3 -4
View File
@@ -11,8 +11,7 @@ import {
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
Theme,
themes,
singleThemes,
} from 'ui'
interface ThemeToggleProps {
@@ -65,9 +64,9 @@ export const ThemeToggle = ({
setTheme(value)
}}
>
{themes
{singleThemes
.filter((x) => x.value === 'dark' || x.value === 'light' || x.value === 'system')
.map((theme: Theme) => (
.map((theme) => (
<DropdownMenuRadioItem key={theme.value} value={theme.value}>
{theme.name}
</DropdownMenuRadioItem>
-1
View File
@@ -37,7 +37,6 @@ export * from './src/components/LoadingLine'
export * from './src/layout/ai-icon-animation'
// theme switcher
export * from './src/components/ThemeProvider/themes'
export * from './src/components/ThemeProvider/singleThemes'
// shadcn
+5 -4
View File
@@ -2,7 +2,6 @@
import React from 'react'
import styleHandler from '../../lib/theme/styleHandler'
import { cn } from '../../lib/utils/cn'
import { IconContext } from './IconContext'
@@ -37,8 +36,6 @@ function IconBase({
icon,
...props
}: Props) {
const __styles = styleHandler('icon')
return (
<IconContext.Consumer>
{({ contextSize, className: contextClassName }) => {
@@ -119,7 +116,11 @@ function IconBase({
IconComponent()
)
return background ? <div className={__styles.container}>{Icon}</div> : Icon
return background ? (
<div className="shrink-0 flex items-center justify-center rounded-full p-3">{Icon}</div>
) : (
Icon
)
}}
</IconContext.Consumer>
)
+19 -16
View File
@@ -1,7 +1,6 @@
import { Loader2 } from 'lucide-react'
import React from 'react'
import styleHandler from '../../lib/theme/styleHandler'
import { cn } from '../../lib/utils/cn'
interface Props {
@@ -10,22 +9,26 @@ interface Props {
isFullHeight?: boolean
}
export default function Loading({ children, active, isFullHeight = false }: Props) {
const __styles = styleHandler('loading')
let classNames = [__styles.base]
let contentClasses = [__styles.content.base]
if (active) {
contentClasses.push(__styles.content.active)
}
let spinnerClasses = [__styles.spinner]
return (
<div className={cn(classNames.join(' '), isFullHeight && 'h-full')}>
<div className={cn(contentClasses.join(' '), isFullHeight && 'h-full')}>{children}</div>
{active && <Loader2 size={24} className={spinnerClasses.join(' ')} />}
<div
className={cn('relative', {
'opacity-40': active,
'h-full': isFullHeight,
})}
>
<div
className={cn('transition-opacity duration-300', {
'h-full': isFullHeight,
})}
>
{children}
</div>
{active && (
<Loader2
size={24}
className="absolute text-foreground-lighter animate-spin inset-0 size-5 m-auto"
/>
)}
</div>
)
}
+76 -34
View File
@@ -1,8 +1,8 @@
'use client'
import { cva } from 'class-variance-authority'
import React from 'react'
import styleHandler from '../../lib/theme/styleHandler'
import { cn } from '../../lib/utils/cn'
import { MenuContextProvider, useMenuContext } from './MenuContext'
@@ -42,45 +42,84 @@ interface ItemProps {
style?: React.CSSProperties
}
export const menuItemVariants = cva(
cn(
'cursor-pointer flex space-x-3 items-center outline-hidden focus-visible:ring-1 ring-foreground-muted focus-visible:z-10 group'
),
{
variants: {
type: {
text: 'py-1',
border: 'px-4 py-1',
pills: 'my-px px-3 py-[3px] rounded-md transition-colors active:bg-sidebar-accent/50',
},
active: {
true: 'font-semibold z-10',
false: 'font-normal border-default group-hover:border-foreground-muted',
},
},
compoundVariants: [
{
type: 'text',
active: true,
className: 'text-foreground-muted',
},
{
type: 'border',
active: true,
className: 'text-foreground-muted border-l border-brand group-hover:border-brand',
},
{
type: 'border',
active: false,
className: 'border-l',
},
{
type: 'pills',
active: true,
className: 'bg-sidebar-accent text-foreground-lighter',
},
{
type: 'pills',
active: false,
className: 'hover:bg-sidebar-accent/50',
},
],
defaultVariants: {
active: false,
},
}
)
export function Item({ children, icon, active, onClick, style }: ItemProps) {
const __styles = styleHandler('menu')
const { type } = useMenuContext()
let classes = [__styles.item.base]
classes.push(__styles.item.variants[type].base)
if (active) {
classes.push(__styles.item.variants[type].active)
} else {
classes.push(__styles.item.variants[type].normal)
}
let contentClasses = [__styles.item.content.base]
if (active) {
contentClasses.push(__styles.item.content.active)
} else {
contentClasses.push(__styles.item.content.normal)
}
let iconClasses = [__styles.item.icon.base]
if (active) {
iconClasses.push(__styles.item.icon.active)
} else {
iconClasses.push(__styles.item.icon.normal)
}
return (
<li
role="menuitem"
className={cn('outline-hidden', classes)}
className={cn('outline-hidden', menuItemVariants({ type, active }))}
style={style}
onClick={onClick}
aria-current={active ? 'page' : undefined}
>
{icon && <div className={`${iconClasses.join(' ')} min-w-fit`}>{icon}</div>}
<span className={contentClasses.join(' ')}>{children}</span>
{icon && (
<div
className={cn('transition truncate text-sm min-w-fit', {
'text-foreground-lighter group-hover:text-foreground-light': !active,
'text-foreground': active,
})}
>
{icon}
</div>
)}
<span
className={cn('transition truncate text-sm', {
'text-foreground-light group-hover:text-foreground': !active,
'text-foreground font-semibold': active,
})}
>
{children}
</span>
</li>
)
}
@@ -92,12 +131,15 @@ interface GroupProps {
}
export function Group({ children, icon, title }: GroupProps) {
const __styles = styleHandler('menu')
const { type } = useMenuContext()
return (
<div className={[__styles.group.base, __styles.group.variants[type]].join(' ')}>
{icon && <span className={__styles.group.icon}>{icon}</span>}
<span className={__styles.group.content}>{title}</span>
<div
className={cn('flex space-x-3 mb-2 font-normal', {
'px-3': type === 'pills',
})}
>
{icon && <span className="text-foreground-lighter">{icon}</span>}
<span className="text-sm text-foreground-lighter w-full">{title}</span>
{children}
</div>
)
@@ -1,11 +1,12 @@
'use client'
import { cva } from 'class-variance-authority'
import { Dialog } from 'radix-ui'
import React from 'react'
import { Button } from '../../components/Button/Button'
import { Tooltip, TooltipContent, TooltipTrigger } from '../../components/shadcn/ui/tooltip'
import styleHandler from '../../lib/theme/styleHandler'
import { cn } from '../../lib/utils'
export type SidePanelProps = RadixProps & CustomProps
@@ -41,6 +42,34 @@ interface CustomProps {
tooltip?: string
}
export const sidePanelContentVariants = cva(
cn('z-50 bg-dash-sidebar flex flex-col fixed inset-y-0 h-full lg:h-screen border-l shadow-xl'),
{
variants: {
size: {
medium: `w-screen max-w-md h-full`,
large: `w-screen max-w-2xl h-full`,
xlarge: `w-screen max-w-3xl h-full`,
xxlarge: `w-screen max-w-4xl h-full`,
xxxlarge: `w-screen max-w-5xl h-full`,
xxxxlarge: `w-screen max-w-6xl h-full`,
},
align: {
left: `
left-0
data-open:animate-panel-slide-left-out
data-closed:animate-panel-slide-left-in
`,
right: `
right-0
data-open:animate-panel-slide-right-out
data-closed:animate-panel-slide-right-in
`,
},
},
}
)
const SidePanel = ({
id,
disabled,
@@ -63,12 +92,10 @@ const SidePanel = ({
tooltip,
...props
}: SidePanelProps) => {
const __styles = styleHandler('sidepanel')
const footerContent = customFooter ? (
customFooter
) : (
<div className={__styles.footer}>
<div className="flex justify-end gap-2 p-4 bg-overlay border-t">
<div>
<Button disabled={loading} variant="default" onClick={() => (onCancel ? onCancel() : null)}>
{cancelText}
@@ -119,14 +146,9 @@ const SidePanel = ({
{triggerElement && <Dialog.Trigger asChild>{triggerElement}</Dialog.Trigger>}
<Dialog.Portal>
<Dialog.Overlay className={__styles.overlay} />
<Dialog.Overlay className="z-50 fixed bg-alternative h-full w-full left-0 top-0 opacity-75 data-closed:animate-fade-out-overlay-bg data-open:animate-fade-in-overlay-bg" />
<Dialog.Content
className={[
__styles.base,
__styles.size[size],
__styles.align[align],
className && className,
].join(' ')}
className={sidePanelContentVariants({ align, size, className })}
onOpenAutoFocus={onOpenAutoFocus}
onCloseAutoFocus={onCloseAutoFocus}
onEscapeKeyDown={onEscapeKeyDown}
@@ -138,8 +160,12 @@ const SidePanel = ({
}}
{...props}
>
{header && <header className={__styles.header}>{header}</header>}
<div className={__styles.contents}>{children}</div>
{header && (
<header className="flex items-center space-y-1 py-4 px-4 bg-dash-sidebar sm:px-6 border-b h-(--header-height)">
{header}
</header>
)}
<div className="relative flex-1 overflow-y-auto">{children}</div>
{!hideFooter && footerContent}
</Dialog.Content>
</Dialog.Portal>
@@ -148,9 +174,7 @@ const SidePanel = ({
}
export function Separator() {
let __styles = styleHandler('sidepanel')
return <div className={__styles.separator}></div>
return <div className="w-full h-px my-2 bg-border"></div>
}
export function Content({
@@ -160,9 +184,7 @@ export function Content({
children: React.ReactNode
className?: string
}) {
let __styles = styleHandler('sidepanel')
return <div className={[__styles.content, className].join(' ').trim()}>{children}</div>
return <div className={cn('px-4 sm:px-6', className)}>{children}</div>
}
SidePanel.Content = Content
+87 -23
View File
@@ -1,5 +1,6 @@
'use client'
import { cva } from 'class-variance-authority'
import { Tabs as TabsPrimitive } from 'radix-ui'
import {
Children,
@@ -11,7 +12,7 @@ import {
type RefObject,
} from 'react'
import styleHandler from '../../lib/theme/styleHandler'
import { cn } from '../../lib/utils'
export interface TabsProps {
type?: 'pills' | 'underlined' | 'cards' | 'rounded-pills'
@@ -39,6 +40,81 @@ interface TabsSubComponents {
Panel: React.FC<PropsWithChildren<PanelProps>>
}
export const tabsListVariants = cva(cn('flex'), {
variants: {
type: {
pills: 'space-x-1',
underlined: 'items-center border-b border-secondary',
cards: '',
'rounded-pills': 'flex-wrap gap-2',
},
scrollable: {
true: 'overflow-auto whitespace-nowrap no-scrollbar mask-fadeout-right',
},
wrappable: {
true: 'flex-wrap',
},
},
})
export const tabsTriggerListVariants = cva(
cn(
'relative cursor-pointer flex items-center space-x-2 text-center transition focus:outline-hidden focus-visible:ring-3 focus-visible:ring-foreground-muted focus-visible:border-foreground-muted'
),
{
variants: {
type: {
pills: 'shadow-xs rounded-sm border',
underlined: 'text-foreground-lighter',
cards: '',
'rounded-pills': 'shadow-xs rounded-full',
},
size: {
tiny: 'text-xs px-2.5 py-1',
small: 'text-base md:text-sm leading-4 px-3 py-2',
medium: 'text-base md:text-sm px-4 py-2',
large: 'text-base px-4 py-2',
xlarge: 'text-base px-6 py-3',
},
block: {
true: 'w-full flex items-center justify-center',
},
isActive: {
false: 'hover:text-foreground',
},
},
compoundVariants: [
{
type: 'pills',
isActive: true,
className: 'bg-selection text-foreground border-stronger',
},
{
type: 'pills',
isActive: false,
className:
'bg-background border-strong hover:border-foreground-muted text-foreground-muted',
},
{
type: 'underlined',
isActive: true,
className: '!text-foreground border-b-2 border-foreground',
},
{
type: 'rounded-pills',
isActive: true,
className: 'bg-foreground text-background border-foreground',
},
{
type: 'rounded-pills',
isActive: false,
className:
'bg-surface-200 hover:bg-surface-300 hover:border-foreground-lighter text-foreground-lighter',
},
],
}
)
/**
* @deprecated Use `import { Tabs_shadcn_ } from "ui"` instead
*/
@@ -80,8 +156,6 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
if (activeId && activeId !== activeTab) setActiveTab(activeId)
}, [activeId])
let __styles = styleHandler('tabs')
function onTabClick(id: string) {
onClick?.(id)
if (id !== activeTab) {
@@ -90,30 +164,19 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
}
}
const listClasses = [__styles[type].list]
if (scrollable) listClasses.push(__styles.scrollable)
if (wrappable) listClasses.push(__styles.wrappable)
if (listClassNames) listClasses.push(listClassNames)
return (
<TabsPrimitive.Root
value={activeTab}
className={[__styles.base, baseClassNames].join(' ')}
className={cn('w-full justify-between space-y-4', baseClassNames)}
ref={refs?.base}
>
<TabsPrimitive.List className={listClasses.join(' ')} ref={refs?.list}>
<TabsPrimitive.List
className={tabsListVariants({ type, scrollable, wrappable, className: listClassNames })}
ref={refs?.list}
>
{addOnBefore}
{children.map((tab) => {
const isActive = activeTab === tab.props.id
const triggerClasses = [__styles[type].base, __styles.size[size]]
if (isActive) {
triggerClasses.push(__styles[type].active)
} else {
triggerClasses.push(__styles[type].inactive)
}
if (block) {
triggerClasses.push(__styles.block)
}
return (
<TabsPrimitive.Trigger
@@ -126,7 +189,7 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
onClick={() => onTabClick(tab.props.id)}
key={`${tab.props.id}-tab-button`}
value={tab.props.id}
className={triggerClasses.join(' ')}
className={tabsTriggerListVariants({ type, isActive, size, block })}
>
{tab.props.icon}
<span>{tab.props.label}</span>
@@ -158,10 +221,11 @@ interface PanelProps {
* @deprecated Use ./TabsContent_Shadcn_ instead
*/
export const Panel: React.FC<PropsWithChildren<PanelProps>> = ({ children, id, className }) => {
let __styles = styleHandler('tabs')
return (
<TabsPrimitive.Content value={id} className={[__styles.content, className].join(' ')}>
<TabsPrimitive.Content
value={id}
className={cn('focus:outline-hidden transition-height', className)}
>
{children}
</TabsPrimitive.Content>
)
@@ -1,19 +0,0 @@
'use client'
import { createContext } from 'react'
import defaultTheme from '../../lib/theme/defaultTheme'
// import useDarkMode from './utils/useDarkMode'
interface ThemeContextInterface {
theme?: any
// mode?: 'light' | 'dark'
// toggleMode?: any
}
export const ThemeContext = createContext<ThemeContextInterface>({
theme: defaultTheme,
// mode: 'light',
// toggleMode: true,
})
@@ -4,8 +4,8 @@ export interface SingleTheme {
}
export const singleThemes = [
{ name: 'System', value: 'system' }, // Classic Supabase light
{ name: 'Dark', value: 'dark' }, // Classic Supabase dark
{ name: 'Light', value: 'light' }, // Classic Supabase light
{ name: 'Classic Dark', value: 'classic-dark' }, // Deep Dark Supabase dark
{ name: 'System', value: 'system' }, // Classic Supabase light
]
@@ -1,11 +0,0 @@
export interface Theme {
name: string
value: string
}
export const themes = [
{ name: 'System', value: 'system' }, // Switches between dark and light
{ name: 'Dark', value: 'dark' }, // Classic Supabase dark
{ name: 'Classic dark', value: 'classic-dark' }, // Deep Dark Supabase dark
{ name: 'Light', value: 'light' }, // Classic Supabase light
]
@@ -1,41 +0,0 @@
const defaults = {
brandBackground: 'bg-black',
size: {
text: {
tiny: 'text-xs',
small: 'text-sm leading-4',
medium: 'text-sm',
large: 'text-base',
xlarge: 'text-base',
},
padding: {
tiny: 'px-2.5 py-1.5',
small: 'px-3 py-2',
medium: 'px-4 py-2',
large: 'px-4 py-2',
xlarge: 'px-6 py-3',
},
},
}
// simple settings
// advanced settings
// Compiled default used for data inputs and buttons
const default__padding_and_text = {
tiny: `${defaults.size.text.tiny} ${defaults.size.padding.tiny}`,
small: `${defaults.size.text.small} ${defaults.size.padding.small}`,
medium: `${defaults.size.text.medium} ${defaults.size.padding.medium}`,
large: `${defaults.size.text.large} ${defaults.size.padding.large}`,
xlarge: `${defaults.size.text.xlarge} ${defaults.size.padding.xlarge}`,
}
export default {
button: {
type: {
primary: `${defaults.brandBackground} text-white hover:bg-brand-600 dark:hover:bg-brand`,
},
},
}
-647
View File
@@ -1,647 +0,0 @@
/*
* Globlal Variables
*
*/
const defaults = {
bg: {
brand: {
primary: 'bg-purple-600',
secondary: 'bg-purple-200',
},
},
text: {
brand: 'text-purple-600',
body: 'text-foreground-light',
title: 'text-foreground',
},
border: {
brand: 'border-brand-600',
primary: 'border-default',
secondary: 'border-secondary',
alternative: 'border-alternative',
},
placeholder: 'placeholder-foreground-muted',
focus: `
outline-hidden
focus:ring-current focus:ring-2
`,
'focus-visible': `
outline-hidden
transition-all
outline-0
focus-visible:outline-4
focus-visible:outline-offset-1
`,
size: {
// buttons, inputs, input labels use these sizes
// text-base on mobile (below md) to avoid zoom on focus
text: {
tiny: 'text-xs',
small: 'text-base md:text-sm leading-4',
medium: 'text-base md:text-sm',
large: 'text-base',
xlarge: 'text-base',
},
// buttons, inputs, input labels use these sizes
padding: {
tiny: 'px-2.5 py-1',
small: 'px-3 py-2',
medium: 'px-4 py-2',
large: 'px-4 py-2',
xlarge: 'px-6 py-3',
},
},
overlay: {
base: `absolute inset-0 bg-background opacity-50`,
container: `fixed inset-0 transition-opacity`,
},
}
const default__padding_and_text = {
tiny: `${defaults.size.text.tiny} ${defaults.size.padding.tiny}`,
small: `${defaults.size.text.small} ${defaults.size.padding.small}`,
medium: `${defaults.size.text.medium} ${defaults.size.padding.medium}`,
large: `${defaults.size.text.large} ${defaults.size.padding.large}`,
xlarge: `${defaults.size.text.xlarge} ${defaults.size.padding.xlarge}`,
}
/*
* Main tailwind utility classes output
*
*/
export default {
/*
* Card
*/
card: {
base: `
bg-surface-100
border
${defaults.border.primary}
flex flex-col
rounded-md shadow-lg overflow-hidden relative
`,
hoverable: 'transition hover:-translate-y-1 hover:shadow-2xl',
head: `px-8 py-6 flex justify-between
border-b
${defaults.border.primary} `,
content: 'p-8',
},
/*
* Tabs
*/
tabs: {
base: `w-full justify-between space-y-4`,
underlined: {
list: `
flex items-center border-b
${defaults.border.secondary}
`,
base: `
relative
cursor-pointer
text-foreground-lighter
flex
items-center
space-x-2
text-center
transition
focus:outline-hidden
focus-visible:ring-3
focus-visible:ring-foreground-muted
focus-visible:border-foreground-muted
`,
inactive: `
hover:text-foreground
`,
active: `
!text-foreground
border-b-2 border-foreground
`,
},
pills: {
list: 'flex space-x-1',
base: `
relative
cursor-pointer
flex
items-center
space-x-2
text-center
transition
shadow-xs
rounded-sm
border
focus:outline-hidden
focus-visible:ring-3
focus-visible:ring-foreground-muted
focus-visible:border-foreground-muted
`,
inactive: `
bg-background
border-strong hover:border-foreground-muted
text-foreground-muted hover:text-foreground
`,
active: `
bg-selection
text-foreground
border-stronger
`,
},
'rounded-pills': {
list: 'flex flex-wrap gap-2',
base: `
relative
cursor-pointer
flex
items-center
space-x-2
text-center
transition
shadow-xs
rounded-full
focus:outline-hidden
focus-visible:ring-3
focus-visible:ring-foreground-muted
focus-visible:border-foreground-muted
`,
inactive: `
bg-surface-200 hover:bg-surface-300
hover:border-foreground-lighter
text-foreground-lighter hover:text-foreground
`,
active: `
bg-foreground
text-background
border-foreground
`,
},
block: 'w-full flex items-center justify-center',
size: {
...default__padding_and_text,
},
scrollable: `overflow-auto whitespace-nowrap no-scrollbar mask-fadeout-right`,
wrappable: `flex-wrap`,
content: `focus:outline-hidden transition-height`,
},
sidepanel: {
base: `
z-50
bg-dash-sidebar
flex flex-col
fixed
inset-y-0
h-full lg:h-screen
border-l
shadow-xl
`,
header: `
flex items-center
space-y-1 py-4 px-4 bg-dash-sidebar sm:px-6
border-b h-(--header-height)
`,
contents: `
relative
flex-1
overflow-y-auto
`,
content: `
px-4 sm:px-6
`,
footer: `
flex justify-end gap-2
p-4 bg-overlay
border-t
`,
size: {
medium: `w-screen max-w-md h-full`,
large: `w-screen max-w-2xl h-full`,
xlarge: `w-screen max-w-3xl h-full`,
xxlarge: `w-screen max-w-4xl h-full`,
xxxlarge: `w-screen max-w-5xl h-full`,
xxxxlarge: `w-screen max-w-6xl h-full`,
},
align: {
left: `
left-0
data-open:animate-panel-slide-left-out
data-closed:animate-panel-slide-left-in
`,
right: `
right-0
data-open:animate-panel-slide-right-out
data-closed:animate-panel-slide-right-in
`,
},
separator: `
w-full
h-px
my-2
bg-border
`,
overlay: `
z-50
fixed
bg-alternative
h-full w-full
left-0
top-0
opacity-75
data-closed:animate-fade-out-overlay-bg
data-open:animate-fade-in-overlay-bg
`,
// this is to reset the button
// it is advised not to change this
trigger: `
border-none bg-transparent p-0 focus:ring-0
`,
},
/*
* Form Layout
*/
form_layout: {
container: 'grid gap-2',
flex: {
left: {
base: 'flex flex-row gap-6',
content: ``,
labels: 'order-2',
data_input: 'order-1',
},
right: {
base: 'flex flex-row gap-6 justify-between',
content: `order-last`,
labels: '',
data_input: 'text-right',
},
},
responsive: 'md:grid md:grid-cols-12',
non_responsive: 'grid grid-cols-12 gap-2',
labels_horizontal_layout: 'flex flex-row space-x-2 justify-between col-span-12',
labels_vertical_layout: 'flex flex-col space-y-2 col-span-4',
data_input_horizontal_layout: 'col-span-12',
non_box_data_input_spacing_vertical: 'my-3',
non_box_data_input_spacing_horizontal: 'my-3 md:mt-0 mb-3',
data_input_vertical_layout: 'col-span-8',
data_input_vertical_layout__align_right: 'text-right',
label: {
base: 'block text-foreground-light',
size: {
...defaults.size.text,
},
},
label_optional: {
base: 'text-foreground-lighter',
size: {
...defaults.size.text,
},
},
description: {
base: 'mt-2 text-foreground-lighter leading-normal',
size: {
...defaults.size.text,
},
},
label_before: {
base: 'text-foreground-lighter ',
size: {
...defaults.size.text,
},
},
label_after: {
base: 'text-foreground-lighter',
size: {
...defaults.size.text,
},
},
error: {
base: `
text-red-900
transition-all
data-show:mt-2
data-show:animate-slide-down-normal
data-hide:animate-slide-up-normal
`,
size: {
...defaults.size.text,
},
},
size: {
tiny: 'text-xs',
small: 'text-base md:text-sm leading-4',
medium: 'text-base md:text-sm',
large: 'text-base',
xlarge: 'text-base',
},
},
/*
* Menu
*/
menu: {
item: {
base: `
cursor-pointer
flex space-x-3 items-center
outline-hidden
focus-visible:ring-1 ring-foreground-muted focus-visible:z-10
group
`,
content: {
base: `transition truncate text-sm w-full`,
normal: `text-foreground-light group-hover:text-foreground`,
active: `text-foreground font-semibold`,
},
icon: {
base: `transition truncate text-sm`,
normal: `text-foreground-lighter group-hover:text-foreground-light`,
active: `text-foreground`,
},
variants: {
text: {
base: `
py-1
`,
normal: `
font-normal
border-default
group-hover:border-foreground-muted`,
active: `
font-semibold
text-foreground-muted
z-10
`,
},
border: {
base: `
px-4 py-1
`,
normal: `
border-l
font-normal
border-default
group-hover:border-foreground-muted`,
active: `
font-semibold
text-foreground-muted
z-10
border-l
border-brand
group-hover:border-brand
`,
rounded: `rounded-md`,
},
pills: {
base: `my-px px-3 py-[3px] rounded-md transition-colors active:bg-sidebar-accent/50`,
normal: `
font-normal
border-default
hover:bg-sidebar-accent/50
group-hover:border-foreground-muted`,
active: `
font-semibold
bg-sidebar-accent
text-foreground-lighter
z-10 rounded-md
`,
},
},
},
group: {
base: `
flex space-x-3
mb-2
font-normal
`,
icon: `text-foreground-lighter`,
content: `text-sm text-foreground-lighter w-full`,
variants: {
text: ``,
pills: `px-3`,
border: ``,
},
},
},
/*
* modal
*/
modal: {
base: `
relative
bg-dash-sidebar
my-4 max-w-screen
border border-overlay
rounded-md
shadow-xl
data-open:animate-overlay-show
data-closed:animate-overlay-hide
`,
header: `
bg-surface-200
space-y-1 py-3 px-4 sm:px-5
border-b border-overlay
flex items-center justify-between
`,
footer: `
flex justify-end gap-2
py-3 px-5
border-t border-overlay
`,
size: {
tiny: `sm:align-middle sm:w-full sm:max-w-xs`,
small: `sm:align-middle sm:w-full sm:max-w-sm`,
medium: `sm:align-middle sm:w-full sm:max-w-lg`,
large: `sm:align-middle sm:w-full md:max-w-xl`,
xlarge: `sm:align-middle sm:w-full md:max-w-3xl`,
xxlarge: `sm:align-middle sm:w-full max-w-screen md:max-w-6xl`,
xxxlarge: `sm:align-middle sm:w-full md:max-w-7xl`,
},
overlay: `
z-40
fixed
bg-alternative
h-full w-full
left-0
top-0
opacity-75
data-closed:animate-fade-out-overlay-bg
data-open:animate-fade-in-overlay-bg
`,
scroll_overlay: `
z-40
fixed
inset-0
grid
place-items-center
overflow-y-auto
data-open:animate-overlay-show data-closed:animate-overlay-hide
`,
separator: `
w-full
h-px
my-2
bg-border-overlay
`,
content: `px-5`,
},
// listbox
listbox: {
base: `
block
box-border
w-full
rounded-md
shadow-xs
text-foreground
border
focus-visible:shadow-md
${defaults.focus}
focus-visible:border-foreground-muted
focus-visible:ring-background-control
${defaults.placeholder}
indent-px
transition-all
bg-none
`,
container: 'relative',
label: `truncate`,
variants: {
standard: `
bg-control
border border-control
aria-expanded:border-foreground-muted
aria-expanded:ring-border-muted
aria-expanded:ring-2
`,
error: `
bg-destructive-200
border border-destructive-500
focus:ring-destructive-400
placeholder:text-destructive-400
`,
},
options_container_animate: `
transition
data-open:animate-slide-down
data-open:opacity-1
data-closed:animate-slide-up
data-closed:opacity-0
`,
options_container: `
bg-overlay
shadow-lg
border border-solid
border-overlay max-h-60
rounded-md py-1 text-base
sm:text-sm z-10 overflow-hidden overflow-y-scroll
origin-dropdown
data-open:animate-dropdown-content-show
data-closed:animate-dropdown-content-hide
`,
with_icon: 'pl-2',
addOnBefore: `
w-full flex flex-row items-center space-x-3
`,
size: {
...default__padding_and_text,
},
disabled: `opacity-50`,
actions_container: 'absolute inset-y-0 right-0 pl-3 pr-1 flex space-x-1 items-center',
chevron_container: 'absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none',
chevron: 'h-5 w-5 text-foreground-muted',
option: `
w-listbox
transition cursor-pointer select-none relative py-2 pl-3 pr-9
text-foreground-light
text-sm
hover:bg-border-overlay
focus:bg-border-overlay
focus:text-foreground
border-none
focus:outline-hidden
`,
option_active: `text-foreground bg-selection`,
option_disabled: `cursor-not-allowed opacity-60`,
option_inner: `flex items-center space-x-3`,
option_check: `absolute inset-y-0 right-0 flex items-center pr-3 text-brand`,
option_check_active: `text-brand`,
option_check_icon: `h-5 w-5`,
},
inputErrorIcon: {
base: `
flex items-center
right-3 pr-2 pl-2
inset-y-0
pointer-events-none
text-red-900
`,
},
inputIconContainer: {
base: `
absolute inset-y-0
left-0 pl-2 flex
items-center pointer-events-none
text-foreground-light
[&_svg]:stroke-[1.5]
`,
size: {
tiny: '[&_svg]:h-[14px] [&_svg]:w-[14px]',
small: '[&_svg]:h-[18px] [&_svg]:w-[18px]',
medium: '[&_svg]:h-[20px] [&_svg]:w-[20px]',
large: '[&_svg]:h-[20px] [&_svg]:w-[20px] pl-3',
xlarge: '[&_svg]:h-[24px] [&_svg]:w-[24px] pl-3',
xxlarge: '[&_svg]:h-[30px] [&_svg]:w-[30px] pl-3',
xxxlarge: '[&_svg]:h-[42px] [&_svg]:w-[42px] pl-3',
},
},
// Icon
icon: {
container: `shrink-0 flex items-center justify-center rounded-full p-3`,
},
loading: {
base: `relative`,
content: {
base: `transition-opacity duration-300`,
active: `opacity-40`,
},
spinner: `
absolute
text-foreground-lighter animate-spin
inset-0
size-5
m-auto
`,
},
} as const
-29
View File
@@ -1,29 +0,0 @@
import { useContext } from 'react'
import { ThemeContext } from './../../components/ThemeProvider/ThemeProvider'
import defaultTheme from './../theme/defaultTheme'
type Keys = keyof typeof defaultTheme
export default function styleHandler(target: Keys) {
let {
theme: { [target]: __styles },
}: any = useContext(ThemeContext)
// console.log(__styles)
// __styles.replace(/\s+/g, ' ')
// console.log('before string', __styles)
// __styles = JSON.stringify(JSON.parse(__styles))
__styles = JSON.stringify(__styles).replace(/\\n/g, '').replace(/\s\s+/g, ' ')
// console.log('string', __styles)
__styles = JSON.parse(__styles)
// console.log('string', __styles)
return __styles
}