From 5a8b078d65e32d0d3f5b8dc8df903814abfd5fca Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 30 Jun 2022 17:16:22 +0200 Subject: [PATCH] feat(module): handle variants with dynamic colors --- docs/nuxt.config.ts | 3 +- src/module.ts | 122 +++--- src/runtime/presets/default.ts | 687 +++++++++++++++++---------------- src/runtime/utils/colors.ts | 36 +- src/runtime/utils/index.ts | 13 + 5 files changed, 439 insertions(+), 422 deletions(-) diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts index 3b901b9f89..f4d51f80ef 100644 --- a/docs/nuxt.config.ts +++ b/docs/nuxt.config.ts @@ -20,7 +20,8 @@ export default defineNuxtConfig({ }, ui: { colors: { - primary: 'blue' + primary: 'blue', + gray: 'zinc' }, preset: { }, diff --git a/src/module.ts b/src/module.ts index 76334e2ebf..ce55ad3aeb 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,9 +1,21 @@ -import { defineNuxtModule, installModule, addComponentsDir, addTemplate, addPlugin, resolveModule, createResolver } from '@nuxt/kit' -import { defu, defuArrayFn } from 'defu' -import type { TailwindConfig } from 'tailwindcss/tailwind-config' +import { defineNuxtModule, installModule, addComponentsDir, addTemplate, addPlugin, createResolver } from '@nuxt/kit' +import { defu } from 'defu' import colors from 'tailwindcss/colors.js' +import type { TailwindConfig } from 'tailwindcss/tailwind-config' import { name, version } from '../package.json' -import { safeColorsAsRegex } from './runtime/utils/colors' +import { colorsAsRegex, excludeColors } from './runtime/utils/colors' +import defaultPreset from './runtime/presets/default' + +// @ts-ignore +delete colors.lightBlue +// @ts-ignore +delete colors.warmGray +// @ts-ignore +delete colors.trueGray +// @ts-ignore +delete colors.coolGray +// @ts-ignore +delete colors.blueGray interface ColorsOptions { /** @@ -12,16 +24,13 @@ interface ColorsOptions { primary?: string /** - * @default 'zinc' + * @default 'gray' */ gray?: string } export interface ModuleOptions { - /** - * @default 'tailwindui' - */ - preset?: string | object + preset?: object /** * @default 'u' @@ -34,11 +43,11 @@ export interface ModuleOptions { } const defaults = { - preset: 'default', + preset: {}, prefix: 'u', colors: { primary: 'indigo', - gray: 'zinc' + gray: 'gray' }, tailwindcss: { theme: {} @@ -57,7 +66,7 @@ export default defineNuxtModule({ }, defaults, async setup (options, nuxt) { - const { preset, prefix, colors: { primary = 'indigo', gray = 'zinc' } = {}, tailwindcss: { theme = {} } = {} } = options + const { preset = {}, prefix, colors: { primary = 'indigo', gray = 'gray' } = {}, tailwindcss: { theme = {} } = {} } = options const { resolve } = createResolver(import.meta.url) @@ -66,21 +75,54 @@ export default defineNuxtModule({ nuxt.options.build.transpile.push(runtimeDir) nuxt.options.build.transpile.push('@popperjs/core', '@headlessui/vue', '@iconify/vue') + // @ts-ignore + nuxt.hook('tailwindcss:config', function (tailwindConfig: TailwindConfig) { + const globalColors = { + ...(tailwindConfig.theme.colors || colors), + ...tailwindConfig.theme.extend?.colors + } + + // @ts-ignore + tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {} + // @ts-ignore + globalColors.primary = tailwindConfig.theme.extend.colors.primary = globalColors[primary] + // @ts-ignore + globalColors.gray = tailwindConfig.theme.extend.colors.gray = globalColors[gray] + + const variantColors = excludeColors(globalColors) + const safeColorsAsRegex = colorsAsRegex(variantColors) + + tailwindConfig.safelist = tailwindConfig.safelist || [] + tailwindConfig.safelist.push(...[{ + pattern: new RegExp(`bg-(${safeColorsAsRegex})-400`) + }, + { + pattern: new RegExp(`bg-(${safeColorsAsRegex})-(100|600|700)`), + variants: ['hover', 'disabled', 'dark'] + }, + { + pattern: new RegExp(`text-(${safeColorsAsRegex})-(100|800)`), + variants: ['dark'] + }, + { + pattern: new RegExp(`ring-(${safeColorsAsRegex})-(500)`), + variants: ['focus'] + }]) + + const ui: object = defu(preset, defaultPreset(variantColors)) + + addTemplate({ + filename: 'ui.mjs', + getContents: () => `export default ${JSON.stringify(ui)}` + }) + }) + await installModule('@nuxtjs/color-mode', { classSuffix: '' }) await installModule('@nuxtjs/tailwindcss', { viewer: false, config: { darkMode: 'class', - theme: defuArrayFn({ - extend: { - colors: { - // @ts-ignore - gray: typeof gray === 'object' ? gray : (colors && colors[gray]), - // @ts-ignore - primary: typeof primary === 'object' ? primary : (colors && colors[primary]) - } - } - }, theme), + theme, plugins: [ require('@tailwindcss/forms'), require('@tailwindcss/line-clamp'), @@ -91,24 +133,8 @@ export default defineNuxtModule({ resolve(runtimeDir, 'components/**/*.{vue,js,ts}'), resolve(runtimeDir, 'presets/*.{mjs,js,ts}') ], - // Safelist dynamic colors used in preset safelist: [ 'dark', - { - pattern: new RegExp(`bg-(${safeColorsAsRegex})-400`) - }, - { - pattern: new RegExp(`bg-(${safeColorsAsRegex})-(100|600|700)`), - variants: ['hover', 'disabled', 'dark'] - }, - { - pattern: new RegExp(`text-(${safeColorsAsRegex})-(100|800)`), - variants: ['dark'] - }, - { - pattern: new RegExp(`ring-(${safeColorsAsRegex})-(500)`), - variants: ['focus'] - }, { pattern: /rounded-(sm|md|lg|xl|2xl|3xl)/, variants: ['sm'] @@ -118,26 +144,6 @@ export default defineNuxtModule({ cssPath: resolve(runtimeDir, 'tailwind.css') }) - const presetsDir = resolve(runtimeDir, './presets') - - let ui: object = (await import(resolveModule(`./${defaults.preset}`, { paths: presetsDir }))).default - try { - if (typeof preset === 'object') { - ui = defu(preset, ui) - } else { - // @ts-ignore - ui = (await import(resolveModule(`./${preset}`, { paths: presetsDir }))).default - } - } catch (e) { - // eslint-disable-next-line no-console - console.warn('Could not load preset file.') - } - - addTemplate({ - filename: 'ui.mjs', - getContents: () => `export default ${JSON.stringify(ui)}` - }) - addPlugin(resolve(runtimeDir, 'plugins', 'toast.client')) addPlugin(resolve(runtimeDir, 'plugins', 'clipboard.client')) diff --git a/src/runtime/presets/default.ts b/src/runtime/presets/default.ts index 833acdabc4..373a13fa2e 100644 --- a/src/runtime/presets/default.ts +++ b/src/runtime/presets/default.ts @@ -1,390 +1,391 @@ -import { safeColors } from '../utils/colors' - -const button = { - base: 'font-medium focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 focus:ring-offset-white dark:focus:ring-offset-black', - rounded: 'rounded-md', - size: { - xxs: 'text-xs', - xs: 'text-xs', - sm: 'text-sm leading-4', - md: 'text-sm', - lg: 'text-base', - xl: 'text-base' - }, - spacing: { - xxs: 'px-2 py-1', - xs: 'px-2.5 py-1.5', - sm: 'px-3 py-2', - md: 'px-4 py-2', - lg: 'px-4 py-2', - xl: 'px-6 py-3' - }, - square: { - xxs: 'p-1', - xs: 'p-1.5', - sm: 'p-2', - md: 'p-2', - lg: 'p-2', - xl: 'p-3' - }, - variant: { - ...safeColors.reduce((acc: any, color) => { - acc[color] = `shadow-sm border border-transparent text-white bg-${color}-600 hover:bg-${color}-700 disabled:bg-${color}-600 focus:ring-2 focus:ring-offset-2 focus:ring-${color}-500` - return acc - }, {}), - secondary: 'border border-transparent text-primary-700 bg-primary-100 hover:bg-primary-200 disabled:bg-primary-100 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', - white: 'shadow-sm border u-border-gray-300 u-text-gray-700 u-bg-white hover:u-bg-gray-50 disabled:u-bg-white focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', - black: 'shadow-sm border border-transparent u-text-white u-bg-gray-800 hover:u-bg-gray-900 disabled:u-bg-gray-800 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', - transparent: 'border border-transparent u-text-gray-500 hover:u-text-gray-700 focus:u-text-gray-700 disabled:hover:u-text-gray-500', - link: 'border border-transparent text-primary-500 hover:text-primary-700 focus:text-primary-700' - }, - icon: { - base: 'flex-shrink-0', - loading: 'heroicons-outline:refresh', +export default (variantColors: string[]) => { + const button = { + base: 'font-medium focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 focus:ring-offset-white dark:focus:ring-offset-black', + rounded: 'rounded-md', size: { - xxs: 'h-3.5 w-3.5', - xs: 'h-4 w-4', - sm: 'h-4 w-4', - md: 'h-5 w-5', - lg: 'h-5 w-5', - xl: 'h-5 w-5' + xxs: 'text-xs', + xs: 'text-xs', + sm: 'text-sm leading-4', + md: 'text-sm', + lg: 'text-base', + xl: 'text-base' }, - leading: { - spacing: { - xxs: '-ml-0.5 mr-1', - xs: '-ml-0.5 mr-1.5', - sm: '-ml-0.5 mr-2', - md: '-ml-1 mr-2', - lg: '-ml-1 mr-3', - xl: '-ml-1 mr-3' - } + spacing: { + xxs: 'px-2 py-1', + xs: 'px-2.5 py-1.5', + sm: 'px-3 py-2', + md: 'px-4 py-2', + lg: 'px-4 py-2', + xl: 'px-6 py-3' }, - trailing: { - spacing: { - xxs: 'ml-1 -mr-0.5', - xs: 'ml-1.5 -mr-0.5', - sm: 'ml-2 -mr-0.5', - md: 'ml-2 -mr-1', - lg: 'ml-3 -mr-1', - xl: 'ml-3 -mr-1' + square: { + xxs: 'p-1', + xs: 'p-1.5', + sm: 'p-2', + md: 'p-2', + lg: 'p-2', + xl: 'p-3' + }, + variant: { + ...variantColors.reduce((acc: any, color: string) => { + acc[color] = `shadow-sm border border-transparent text-white bg-${color}-600 hover:bg-${color}-700 disabled:bg-${color}-600 focus:ring-2 focus:ring-offset-2 focus:ring-${color}-500` + return acc + }, {}), + secondary: 'border border-transparent text-primary-700 bg-primary-100 hover:bg-primary-200 disabled:bg-primary-100 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', + white: 'shadow-sm border u-border-gray-300 u-text-gray-700 u-bg-white hover:u-bg-gray-50 disabled:u-bg-white focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', + gray: 'shadow-sm border u-border-gray-300 u-text-gray-700 u-bg-gray-50 hover:u-bg-gray-100 disabled:u-bg-gray-50 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', + black: 'shadow-sm border border-transparent u-text-white u-bg-gray-800 hover:u-bg-gray-900 disabled:u-bg-gray-800 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500', + transparent: 'border border-transparent u-text-gray-500 hover:u-text-gray-700 focus:u-text-gray-700 disabled:hover:u-text-gray-500', + link: 'border border-transparent text-primary-500 hover:text-primary-700 focus:text-primary-700' + }, + icon: { + base: 'flex-shrink-0', + loading: 'heroicons-outline:refresh', + size: { + xxs: 'h-3.5 w-3.5', + xs: 'h-4 w-4', + sm: 'h-4 w-4', + md: 'h-5 w-5', + lg: 'h-5 w-5', + xl: 'h-5 w-5' + }, + leading: { + spacing: { + xxs: '-ml-0.5 mr-1', + xs: '-ml-0.5 mr-1.5', + sm: '-ml-0.5 mr-2', + md: '-ml-1 mr-2', + lg: '-ml-1 mr-3', + xl: '-ml-1 mr-3' + } + }, + trailing: { + spacing: { + xxs: 'ml-1 -mr-0.5', + xs: 'ml-1.5 -mr-0.5', + sm: 'ml-2 -mr-0.5', + md: 'ml-2 -mr-1', + lg: 'ml-3 -mr-1', + xl: 'ml-3 -mr-1' + } } } } -} -const badge = { - base: 'inline-flex items-center font-medium', - size: { - sm: 'text-xs px-2 py-0.5', - md: 'text-sm px-2.5 py-0.5', - lg: 'text-sm px-3 py-0.5', - xl: 'text-sm px-4 py-1' - }, - variant: { - ...safeColors.reduce((acc: any, color) => { - acc[color] = `bg-${color}-100 dark:bg-${color}-700 text-${color}-800 dark:text-${color}-100` - return acc - }, {}) + const badge = { + base: 'inline-flex items-center font-medium', + size: { + sm: 'text-xs px-2 py-0.5', + md: 'text-sm px-2.5 py-0.5', + lg: 'text-sm px-3 py-0.5', + xl: 'text-sm px-4 py-1' + }, + variant: { + ...variantColors.reduce((acc: any, color: string) => { + acc[color] = `bg-${color}-100 dark:bg-${color}-700 text-${color}-800 dark:text-${color}-100` + return acc + }, {}) + } } -} -const formGroup = { - wrapper: '', - label: 'block text-sm font-medium u-text-gray-700', - labelWrapper: 'flex content-center justify-between', - container: 'mt-1 relative', - required: 'text-red-400', - description: 'text-sm leading-5 u-text-gray-500', - hint: 'text-sm leading-5 u-text-gray-500', - help: 'mt-2 text-sm u-text-gray-500' -} + const formGroup = { + wrapper: '', + label: 'block text-sm font-medium u-text-gray-700', + labelWrapper: 'flex content-center justify-between', + container: 'mt-1 relative', + required: 'text-red-400', + description: 'text-sm leading-5 u-text-gray-500', + hint: 'text-sm leading-5 u-text-gray-500', + help: 'mt-2 text-sm u-text-gray-500' + } -const input = { - wrapper: 'relative', - base: 'relative block w-full disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none', - size: { - xxs: 'text-xs', - xs: 'text-xs', - sm: 'text-sm leading-4', - md: 'text-sm', - lg: 'text-base', - xl: 'text-base' - }, - spacing: { - xxs: 'px-1 py-0.5', - xs: 'px-2.5 py-1.5', - sm: 'px-3 py-2', - md: 'px-4 py-2', - lg: 'px-4 py-2', - xl: 'px-6 py-3' - }, - leading: { - spacing: { - xxs: 'pl-7', - xs: 'pl-7', - sm: 'pl-10', - md: 'pl-10', - lg: 'pl-10', - xl: 'pl-10' - } - }, - trailing: { - spacing: { - xxs: 'pr-7', - xs: 'pr-7', - sm: 'pr-10', - md: 'pr-10', - lg: 'pr-10', - xl: 'pr-10' - } - }, - appearance: { - default: 'u-bg-white u-text-gray-700 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 dark:focus:border-primary-500 border u-border-gray-300 rounded-md shadow-sm', - none: 'border-0 bg-transparent focus:ring-0 focus:shadow-none' - }, - icon: { - base: 'u-text-gray-400', - loading: 'heroicons-outline:refresh', + const input = { + wrapper: 'relative', + base: 'relative block w-full disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none', size: { - xxs: 'h-3 w-3', - xs: 'h-4 w-4', - sm: 'h-5 w-5', - md: 'h-5 w-5', - lg: 'h-5 w-5', - xl: 'h-5 w-5' + xxs: 'text-xs', + xs: 'text-xs', + sm: 'text-sm leading-4', + md: 'text-sm', + lg: 'text-base', + xl: 'text-base' + }, + spacing: { + xxs: 'px-1 py-0.5', + xs: 'px-2.5 py-1.5', + sm: 'px-3 py-2', + md: 'px-4 py-2', + lg: 'px-4 py-2', + xl: 'px-6 py-3' }, leading: { - wrapper: 'absolute inset-y-0 left-0 flex items-center pointer-events-none', spacing: { - xxs: 'ml-2', - xs: 'ml-2', - sm: 'ml-2', - md: 'ml-3', - lg: 'ml-3', - xl: 'ml-3' + xxs: 'pl-7', + xs: 'pl-7', + sm: 'pl-10', + md: 'pl-10', + lg: 'pl-10', + xl: 'pl-10' } }, trailing: { - wrapper: 'absolute inset-y-0 right-0 flex items-center pointer-events-none', spacing: { - xxs: 'mr-2', - xs: 'mr-2', - sm: 'mr-2', - md: 'mr-3', - lg: 'mr-3', - xl: 'mr-3' + xxs: 'pr-7', + xs: 'pr-7', + sm: 'pr-10', + md: 'pr-10', + lg: 'pr-10', + xl: 'pr-10' + } + }, + appearance: { + default: 'u-bg-white u-text-gray-700 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 dark:focus:border-primary-500 border u-border-gray-300 rounded-md shadow-sm', + none: 'border-0 bg-transparent focus:ring-0 focus:shadow-none' + }, + icon: { + base: 'u-text-gray-400', + loading: 'heroicons-outline:refresh', + size: { + xxs: 'h-3 w-3', + xs: 'h-4 w-4', + sm: 'h-5 w-5', + md: 'h-5 w-5', + lg: 'h-5 w-5', + xl: 'h-5 w-5' + }, + leading: { + wrapper: 'absolute inset-y-0 left-0 flex items-center pointer-events-none', + spacing: { + xxs: 'ml-2', + xs: 'ml-2', + sm: 'ml-2', + md: 'ml-3', + lg: 'ml-3', + xl: 'ml-3' + } + }, + trailing: { + wrapper: 'absolute inset-y-0 right-0 flex items-center pointer-events-none', + spacing: { + xxs: 'mr-2', + xs: 'mr-2', + sm: 'mr-2', + md: 'mr-3', + lg: 'mr-3', + xl: 'mr-3' + } } } } -} -const textarea = { - ...input -} + const textarea = { + ...input + } -const select = { - ...input -} + const select = { + ...input + } -const selectCustom = { - ...select, - base: `${select.base} text-left cursor-default`, - list: { - base: 'absolute z-10 mt-1 w-full u-bg-white shadow-lg max-h-60 rounded-md py-1 text-sm ring-1 u-ring-gray-200 overflow-auto focus:outline-none', - option: { - base: 'cursor-default select-none relative py-2 pl-4 pr-10', - active: 'text-white bg-primary-600', - inactive: 'u-text-gray-900', - disabled: 'cursor-not-allowed opacity-50', - icon: { - base: 'absolute inset-y-0 right-0 flex items-center pr-4', - active: 'text-white', - inactive: 'text-primary-600', - size: 'h-5 w-5' + const selectCustom = { + ...select, + base: `${select.base} text-left cursor-default`, + list: { + base: 'absolute z-10 mt-1 w-full u-bg-white shadow-lg max-h-60 rounded-md py-1 text-sm ring-1 u-ring-gray-200 overflow-auto focus:outline-none', + option: { + base: 'cursor-default select-none relative py-2 pl-4 pr-10', + active: 'text-white bg-primary-600', + inactive: 'u-text-gray-900', + disabled: 'cursor-not-allowed opacity-50', + icon: { + base: 'absolute inset-y-0 right-0 flex items-center pr-4', + active: 'text-white', + inactive: 'text-primary-600', + size: 'h-5 w-5' + } } } } -} -const radio = { - wrapper: 'relative flex items-start', - base: 'h-4 w-4 text-primary-600 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 dark:checked:border-primary-600 disabled:opacity-50 disabled:cursor-not-allowed', - label: 'font-medium u-text-gray-700', - required: 'text-red-400', - help: 'u-text-gray-500' -} + const radio = { + wrapper: 'relative flex items-start', + base: 'h-4 w-4 text-primary-600 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 dark:checked:border-primary-600 disabled:opacity-50 disabled:cursor-not-allowed', + label: 'font-medium u-text-gray-700', + required: 'text-red-400', + help: 'u-text-gray-500' + } -const checkbox = { - ...radio, - base: `${radio.base} rounded` -} + const checkbox = { + ...radio, + base: `${radio.base} rounded` + } -const card = { - base: 'overflow-hidden', - background: 'u-bg-white', - border: 'u-border-gray-200', - ring: 'ring-1 u-ring-gray-200', - rounded: 'rounded-md', - shadow: 'shadow', - body: 'px-4 py-5 sm:p-6', - header: 'px-4 py-5 sm:px-6', - footer: 'px-4 py-4 sm:px-6' -} + const card = { + base: 'overflow-hidden', + background: 'u-bg-white', + border: 'u-border-gray-200', + ring: 'ring-1 u-ring-gray-200', + rounded: 'rounded-md', + shadow: 'shadow', + body: 'px-4 py-5 sm:p-6', + header: 'px-4 py-5 sm:px-6', + footer: 'px-4 py-4 sm:px-6' + } -const modal = { - base: 'relative inline-block align-bottom text-left overflow-hidden transform transition-all sm:my-8 sm:align-middle w-full', - background: 'u-bg-white', - border: '', - ring: '', - rounded: 'rounded-lg', - shadow: 'shadow-xl', - width: 'sm:max-w-lg' -} + const modal = { + base: 'relative inline-block align-bottom text-left overflow-hidden transform transition-all sm:my-8 sm:align-middle w-full', + background: 'u-bg-white', + border: '', + ring: '', + rounded: 'rounded-lg', + shadow: 'shadow-xl', + width: 'sm:max-w-lg' + } -const container = { - constrained: 'max-w-7xl' -} + const container = { + constrained: 'max-w-7xl' + } -const toggle = { - base: 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black', - active: 'bg-primary-600', - inactive: 'u-bg-gray-200', - container: { - base: 'pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200', - active: 'translate-x-5', - inactive: 'translate-x-0' - }, - icon: { - base: 'absolute inset-0 h-full w-full flex items-center justify-center transition-opacity', - active: 'opacity-100 ease-in duration-200', - inactive: 'opacity-0 ease-out duration-100', - on: 'h-3 w-3 text-primary-600', - off: 'h-3 w-3 u-text-gray-400' + const toggle = { + base: 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black', + active: 'bg-primary-600', + inactive: 'u-bg-gray-200', + container: { + base: 'pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200', + active: 'translate-x-5', + inactive: 'translate-x-0' + }, + icon: { + base: 'absolute inset-0 h-full w-full flex items-center justify-center transition-opacity', + active: 'opacity-100 ease-in duration-200', + inactive: 'opacity-0 ease-out duration-100', + on: 'h-3 w-3 text-primary-600', + off: 'h-3 w-3 u-text-gray-400' + } } -} -const verticalNavigation = { - wrapper: 'space-y-1', - base: 'group flex items-center text-sm font-medium rounded-md w-full relative', - spacing: 'px-3 py-2', - active: 'u-text-gray-900 u-bg-gray-100', - inactive: 'u-text-gray-600 hover:u-text-gray-900 hover:u-bg-gray-50 focus:u-bg-gray-50', - icon: { - base: 'flex-shrink-0 h-6 w-6', - spacing: '-ml-1 mr-3', - active: 'u-text-gray-500', - inactive: 'u-text-gray-400 group-hover:u-text-gray-500' - }, - avatar: { - base: 'flex-shrink-0', - spacing: '-ml-1 mr-3' - }, - badge: { - base: 'ml-auto inline-block py-0.5 px-3 text-xs rounded-full', - active: 'u-bg-white', - inactive: 'u-bg-gray-100 u-text-gray-600 group-hover:u-bg-gray-200' + const verticalNavigation = { + wrapper: 'space-y-1', + base: 'group flex items-center text-sm font-medium rounded-md w-full relative', + spacing: 'px-3 py-2', + active: 'u-text-gray-900 u-bg-gray-100', + inactive: 'u-text-gray-600 hover:u-text-gray-900 hover:u-bg-gray-50 focus:u-bg-gray-50', + icon: { + base: 'flex-shrink-0 h-6 w-6', + spacing: '-ml-1 mr-3', + active: 'u-text-gray-500', + inactive: 'u-text-gray-400 group-hover:u-text-gray-500' + }, + avatar: { + base: 'flex-shrink-0', + spacing: '-ml-1 mr-3' + }, + badge: { + base: 'ml-auto inline-block py-0.5 px-3 text-xs rounded-full', + active: 'u-bg-white', + inactive: 'u-bg-gray-100 u-text-gray-600 group-hover:u-bg-gray-200' + } } -} -const alertDialog = { - icon: { - wrapper: 'h-12 w-12 sm:h-10 sm:w-10 bg-primary-100', - base: 'text-primary-600' - }, - title: 'text-lg leading-6 font-medium text-gray-900', - description: 'text-sm text-gray-500' -} + const alertDialog = { + icon: { + wrapper: 'h-12 w-12 sm:h-10 sm:w-10 bg-primary-100', + base: 'text-primary-600' + }, + title: 'text-lg leading-6 font-medium text-gray-900', + description: 'text-sm text-gray-500' + } -const dropdown = { - wrapper: 'relative inline-flex text-left', - container: 'w-48 z-20 py-2', - base: 'u-bg-white divide-y u-divide-gray-100 rounded-md ring-1 u-ring-gray-200 shadow-lg', - item: { - base: 'group flex items-center gap-3 px-4 py-2 text-sm w-full', - active: 'u-bg-gray-100 u-text-gray-900', - inactive: 'u-text-gray-700', - disabled: 'cursor-not-allowed opacity-50', - icon: 'h-5 w-5 u-text-gray-400 group-hover:u-text-gray-500 flex-shrink-0', - avatar: '-m-0.5 group-hover:u-bg-gray-200 flex-shrink-0' + const dropdown = { + wrapper: 'relative inline-flex text-left', + container: 'w-48 z-20 py-2', + base: 'u-bg-white divide-y u-divide-gray-100 rounded-md ring-1 u-ring-gray-200 shadow-lg', + item: { + base: 'group flex items-center gap-3 px-4 py-2 text-sm w-full', + active: 'u-bg-gray-100 u-text-gray-900', + inactive: 'u-text-gray-700', + disabled: 'cursor-not-allowed opacity-50', + icon: 'h-5 w-5 u-text-gray-400 group-hover:u-text-gray-500 flex-shrink-0', + avatar: '-m-0.5 group-hover:u-bg-gray-200 flex-shrink-0' + } } -} -const tabs = { - wrapper: 'flex items-center gap-8', - base: 'whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm', - active: 'border-primary-500 text-primary-600', - inactive: 'border-transparent u-text-gray-500 hover:u-text-gray-700 hover:u-border-gray-300' -} + const tabs = { + wrapper: 'flex items-center gap-8', + base: 'whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm', + active: 'border-primary-500 text-primary-600', + inactive: 'border-transparent u-text-gray-500 hover:u-text-gray-700 hover:u-border-gray-300' + } -const pills = { - wrapper: 'flex items-center gap-4', - base: 'px-3 py-2 font-medium text-sm rounded-md', - active: 'u-bg-gray-100 u-text-gray-700', - inactive: 'u-text-gray-500 hover:u-text-gray-700' -} + const pills = { + wrapper: 'flex items-center gap-4', + base: 'px-3 py-2 font-medium text-sm rounded-md', + active: 'u-bg-gray-100 u-text-gray-700', + inactive: 'u-text-gray-500 hover:u-text-gray-700' + } -const avatar = { - wrapper: 'relative inline-flex items-center justify-center', - background: 'u-bg-gray-100', - rounded: 'rounded-md', - placeholder: 'text-xs font-medium leading-none u-text-black truncate', - size: { - xxxs: 'h-4 w-4 text-xs', - xxs: 'h-5 w-5 text-xs', - xs: 'h-6 w-6 text-xs', - sm: 'h-8 w-8 text-sm', - md: 'h-10 w-10 text-md', - lg: 'h-12 w-12 text-lg', - xl: 'h-14 w-14 text-xl', - '2xl': 'h-16 w-16 text-2xl', - '3xl': 'h-20 w-20 text-3xl' - }, - chip: { - base: 'absolute block rounded-full ring-2 u-ring-white', - position: { - 'top-right': 'top-0 right-0', - 'bottom-right': 'bottom-0 right-0', - 'top-left': 'top-0 left-0', - 'bottom-left': 'bottom-0 left-0' - }, - variant: { - ...safeColors.reduce((acc: any, color) => { - acc[color] = `bg-${color}-400` - return acc - }, {}) - }, + const avatar = { + wrapper: 'relative inline-flex items-center justify-center', + background: 'u-bg-gray-100', + rounded: 'rounded-md', + placeholder: 'text-xs font-medium leading-none u-text-black truncate', size: { - xxxs: 'h-1 w-1', - xxs: 'h-1 w-1', - xs: 'h-1.5 w-1.5', - sm: 'h-2 w-2', - md: 'h-2.5 w-2.5', - lg: 'h-3 w-3', - xl: 'h-3.5 w-3.5', - '2xl': 'h-3.5 w-3.5', - '3xl': 'h-4 w-4' + xxxs: 'h-4 w-4 text-xs', + xxs: 'h-5 w-5 text-xs', + xs: 'h-6 w-6 text-xs', + sm: 'h-8 w-8 text-sm', + md: 'h-10 w-10 text-md', + lg: 'h-12 w-12 text-lg', + xl: 'h-14 w-14 text-xl', + '2xl': 'h-16 w-16 text-2xl', + '3xl': 'h-20 w-20 text-3xl' + }, + chip: { + base: 'absolute block rounded-full ring-2 u-ring-white', + position: { + 'top-right': 'top-0 right-0', + 'bottom-right': 'bottom-0 right-0', + 'top-left': 'top-0 left-0', + 'bottom-left': 'bottom-0 left-0' + }, + variant: { + ...variantColors.reduce((acc: any, color: string) => { + acc[color] = `bg-${color}-400` + return acc + }, {}) + }, + size: { + xxxs: 'h-1 w-1', + xxs: 'h-1 w-1', + xs: 'h-1.5 w-1.5', + sm: 'h-2 w-2', + md: 'h-2.5 w-2.5', + lg: 'h-3 w-3', + xl: 'h-3.5 w-3.5', + '2xl': 'h-3.5 w-3.5', + '3xl': 'h-4 w-4' + } } } -} -export default { - card, - modal, - button, - badge, - formGroup, - input, - textarea, - select, - selectCustom, - checkbox, - radio, - container, - toggle, - verticalNavigation, - alertDialog, - dropdown, - tabs, - pills, - avatar + return { + card, + modal, + button, + badge, + formGroup, + input, + textarea, + select, + selectCustom, + checkbox, + radio, + container, + toggle, + verticalNavigation, + alertDialog, + dropdown, + tabs, + pills, + avatar + } } diff --git a/src/runtime/utils/colors.ts b/src/runtime/utils/colors.ts index f7edfdab6a..78524237c1 100644 --- a/src/runtime/utils/colors.ts +++ b/src/runtime/utils/colors.ts @@ -1,22 +1,18 @@ -export const safeColors = [ - 'primary', - 'rose', - 'pink', - 'fuchsia', - 'purple', - 'violet', - 'indigo', - 'blue', - 'sky', - 'cyan', - 'teal', - 'emerald', - 'green', - 'lime', - 'yellow', - 'amber', - 'orange', - 'red' +import { omit, kebabCase } from './index' + +export const colorsToExclude = [ + 'inherit', + 'transparent', + 'current', + 'white', + 'black', + 'slate', + 'gray', + 'zinc', + 'neutral', + 'stone' ] -export const safeColorsAsRegex = safeColors.join('|') +export const excludeColors = (colors: object) => Object.keys(omit(colors, colorsToExclude)).map(color => kebabCase(color)) + +export const colorsAsRegex = (colors: string[]) => colors.join('|') diff --git a/src/runtime/utils/index.ts b/src/runtime/utils/index.ts index bb2d7740b0..6414388bae 100644 --- a/src/runtime/utils/index.ts +++ b/src/runtime/utils/index.ts @@ -3,3 +3,16 @@ export * from './popper' export function classNames (...classes: any[string]) { return classes.filter(Boolean).join(' ') } + +export const kebabCase = (str: string) => { + return str + ?.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + ?.map(x => x.toLowerCase()) + ?.join('-') +} + +export const omit = (obj: object, keys: string[]) => { + return Object.fromEntries( + Object.entries(obj).filter(([key]) => !keys.includes(key)) + ) +}