diff --git a/package.json b/package.json index c29c1b6..d7c50cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soda-material", - "version": "0.0.26", + "version": "0.0.27", "type": "module", "types": "dist", "main": "dist/index.js", @@ -25,30 +25,30 @@ "@material/material-color-utilities": "^0.2.7", "@mdi/js": "^7.4.47", "@mdi/react": "^1.6.1", - "@storybook/addon-essentials": "^8.0.9", - "@storybook/addon-interactions": "^8.0.9", - "@storybook/addon-links": "^8.0.9", - "@storybook/blocks": "^8.0.9", - "@storybook/react": "^8.0.9", - "@storybook/react-vite": "^8.0.9", + "@storybook/addon-essentials": "^8.0.10", + "@storybook/addon-interactions": "^8.0.10", + "@storybook/addon-links": "^8.0.10", + "@storybook/blocks": "^8.0.10", + "@storybook/react": "^8.0.10", + "@storybook/react-vite": "^8.0.10", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/parser": "^7.8.0", "@vitejs/plugin-react": "^4.2.1", - "chromatic": "^11.3.0", + "chromatic": "^11.3.2", "clsx": "^2.1.1", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.6", + "eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-storybook": "^0.8.0", - "glob": "^10.3.12", + "glob": "^10.3.14", "prettier": "^3.2.5", - "sass": "^1.76.0", - "storybook": "^8.0.9", - "styled-jsx": "^5.1.2", + "sass": "^1.77.1", + "storybook": "^8.0.10", + "styled-jsx": "^5.1.3", "typescript": "^5.4.5", - "vite": "^5.2.10" + "vite": "^5.2.11" }, "publishConfig": { "registry": "https://registry.npmjs.org" diff --git a/src/components/tabs/Tabs.tsx b/src/components/tabs/Tabs.tsx index bea3c8d..883c327 100644 --- a/src/components/tabs/Tabs.tsx +++ b/src/components/tabs/Tabs.tsx @@ -1,6 +1,6 @@ import './tabs.scss' import clsx from 'clsx' -import { forwardRef, useEffect, useRef, useState } from 'react' +import { forwardRef, useCallback, useEffect, useRef, useState } from 'react' import { useAutoState } from '@/hooks/use-auto-state' import { useMergeRefs } from '@/hooks/use-merge' import { Ripple } from '@/ripple/Ripple' @@ -59,7 +59,7 @@ export const Tabs = forwardRef< const [left, setLeft] = useState('0') const [width, setWidth] = useState('36px') - useEffect(() => { + const correctlyPlaceIndicator = useCallback(() => { const tabs = ref.current if (!tabs) return const item = tabs.querySelectorAll('.sd-tabs-item')[ @@ -96,6 +96,14 @@ export const Tabs = forwardRef< } }, [index, variant]) + useEffect(correctlyPlaceIndicator, [correctlyPlaceIndicator]) + + useEffect(() => { + window.addEventListener('resize', correctlyPlaceIndicator) + return () => + window.removeEventListener('resize', correctlyPlaceIndicator) + }, [correctlyPlaceIndicator]) + return (
+ +# Dynamic Color + +```ts +// provide some helper function +import { type Theme, themeFromHexString, themeFromImageOrFile } from "soda-material/dist/utils/theme.js" +const theme: Theme = themeFromHexString('#6750a4') +const theme: Theme = await themeFromImageOrFile(/* accept HTMLImageElement or File object */) + +// apply theme +import { applyThemeForSoda } from 'soda-material/dist/utils/theme.js' +applyThemeForSoda('#6750a4') +applyThemeForSoda(theme) +``` + +It's actually a module that provides some helper function and re-export the [@material/material-color-utilities](https://npm.im/@material/material-color-utilities) package. + +# Color Schemes + +Refer to [https://m3.material.io/styles/color/static/baseline](https://m3.material.io/styles/color/static/baseline) + + diff --git a/src/documentation/ColorSchemes.tsx b/src/documentation/ColorSchemes.tsx new file mode 100644 index 0000000..578c2ec --- /dev/null +++ b/src/documentation/ColorSchemes.tsx @@ -0,0 +1,72 @@ +const COLOR_TOKEN_MAP = { + 'surface-tint': ['', 'color'], + 'on-error': ['', 'container'], + error: ['', 'container'], + 'on-tertiary': ['', 'container'], + tertiary: ['', 'container'], + shadow: [''], + outline: ['', 'variant'], + 'on-background': [''], + background: [''], + 'inverse-on-surface': [''], + 'inverse-surface': [''], + 'on-surface': ['', 'variant'], + surface: [ + '', + 'variant', + 'container', + 'container-lowest', + 'container-low', + 'container-high', + 'container-highest', + ], + 'on-secondary': ['', 'container'], + secondary: ['', 'container'], + 'inverse-primary': [''], + 'on-primary': ['', 'container'], + primary: ['', 'container'], +} + +export function ColorStatic() { + return Object.entries(COLOR_TOKEN_MAP).map(([prefix, decorates]) => ( +
+
    + {decorates.map((decorate) => { + const name = decorate ? `${prefix}-${decorate}` : prefix + const color = `var(--md-sys-color-${name})` + return ( +
  • + + {color} + +
  • + ) + })} +
+
+ )) +} diff --git a/src/documentation/ThemingExample.tsx b/src/documentation/ThemingExample.tsx index 63506d0..b183f22 100644 --- a/src/documentation/ThemingExample.tsx +++ b/src/documentation/ThemingExample.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { Fragment, useEffect, useRef, useState } from 'react' import { mdiCheckboxOutline, mdiDeleteOutline, @@ -19,7 +19,6 @@ import { } from '@mdi/js' import Icon from '@mdi/react' import { - Badge, BottomAppBar, BottomSheet, BottomSheetHandle, @@ -53,7 +52,6 @@ import { useFullscreen } from '@/hooks/use-fullscreen' import { usePrefersDark, useWindowSizeType } from '@/hooks/use-media-query' import { applyThemeForSoda, - argbFromHex, themeFromHexString, themeFromImageOrFile, } from '@/utils/theme' @@ -66,13 +64,8 @@ export function ThemingExample() { const [sourceColor, setSourceColor] = useState( localStorage.getItem('sourceColor') || '#6750a4', ) - const [customColors, setCustomColors] = useState([]) + const [file, setFile] = useState(null) - const customColorArray = customColors.map((color, index) => ({ - name: `custom-${index}`, - value: argbFromHex(color), - blend: true, - })) useEffect(() => { localStorage.setItem('sourceColor', sourceColor) @@ -82,25 +75,26 @@ export function ThemingExample() { // eslint-disable-next-line no-extra-semi ;(async () => { const theme = file - ? await themeFromImageOrFile(file, customColorArray) - : themeFromHexString(sourceColor, customColorArray) + ? await themeFromImageOrFile(file) + : themeFromHexString(sourceColor) applyThemeForSoda(theme, prefersDark) })() - }, [customColorArray, prefersDark, sourceColor, file]) + }, [prefersDark, sourceColor, file]) return ( -
-

SourceColor

- +

Change Theme

+

+ Manually select a color: +
+

+

+ Dynamic color from image: +
-

- -
-

CustomColors

- - {customColors.map((customColor, index) => ( -
- { - setCustomColors( - customColors.toSpliced( - index, - 1, - e.target.value, - ), - ) - }} - /> - - - -
- ))} - - -
+

) } @@ -544,7 +492,10 @@ function WithTabs({ variant="secondary" /> {tabs.map( - ({ children, ...item }) => item.value === value && children, + ({ children, ...item }) => + item.value === value && ( + {children} + ), )} )