@just-web/toolkits 1.0.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/_internal/utils/try-parse-json.cjs +14 -0
- package/dist/_internal/utils/try-parse-json.cjs.map +1 -0
- package/dist/_internal/utils/try-parse-json.mjs +13 -0
- package/dist/_internal/utils/try-parse-json.mjs.map +1 -0
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/attributes/data-attribute.d.cts +17 -0
- package/dist/attributes/data-attribute.d.cts.map +1 -0
- package/dist/attributes/data-attribute.d.mts +17 -0
- package/dist/attributes/data-attribute.d.mts.map +1 -0
- package/dist/attributes/get-attribute.cjs +26 -0
- package/dist/attributes/get-attribute.cjs.map +1 -0
- package/dist/attributes/get-attribute.d.cts +21 -0
- package/dist/attributes/get-attribute.d.cts.map +1 -0
- package/dist/attributes/get-attribute.d.mts +21 -0
- package/dist/attributes/get-attribute.d.mts.map +1 -0
- package/dist/attributes/get-attribute.mjs +25 -0
- package/dist/attributes/get-attribute.mjs.map +1 -0
- package/dist/attributes/get-data-attribute.cjs +19 -0
- package/dist/attributes/get-data-attribute.cjs.map +1 -0
- package/dist/attributes/get-data-attribute.d.cts +17 -0
- package/dist/attributes/get-data-attribute.d.cts.map +1 -0
- package/dist/attributes/get-data-attribute.d.mts +17 -0
- package/dist/attributes/get-data-attribute.d.mts.map +1 -0
- package/dist/attributes/get-data-attribute.mjs +19 -0
- package/dist/attributes/get-data-attribute.mjs.map +1 -0
- package/dist/attributes/observe-attribute.cjs +40 -0
- package/dist/attributes/observe-attribute.cjs.map +1 -0
- package/dist/attributes/observe-attribute.d.cts +23 -0
- package/dist/attributes/observe-attribute.d.cts.map +1 -0
- package/dist/attributes/observe-attribute.d.mts +23 -0
- package/dist/attributes/observe-attribute.d.mts.map +1 -0
- package/dist/attributes/observe-attribute.mjs +39 -0
- package/dist/attributes/observe-attribute.mjs.map +1 -0
- package/dist/attributes/observe-data-attribute.cjs +31 -0
- package/dist/attributes/observe-data-attribute.cjs.map +1 -0
- package/dist/attributes/observe-data-attribute.d.cts +26 -0
- package/dist/attributes/observe-data-attribute.d.cts.map +1 -0
- package/dist/attributes/observe-data-attribute.d.mts +26 -0
- package/dist/attributes/observe-data-attribute.d.mts.map +1 -0
- package/dist/attributes/observe-data-attribute.mjs +31 -0
- package/dist/attributes/observe-data-attribute.mjs.map +1 -0
- package/dist/children/just-children.d.cts +37 -0
- package/dist/children/just-children.d.cts.map +1 -0
- package/dist/children/just-children.d.mts +37 -0
- package/dist/children/just-children.d.mts.map +1 -0
- package/dist/children/resolve-children.cjs +11 -0
- package/dist/children/resolve-children.cjs.map +1 -0
- package/dist/children/resolve-children.d.cts +9 -0
- package/dist/children/resolve-children.d.cts.map +1 -0
- package/dist/children/resolve-children.d.mts +9 -0
- package/dist/children/resolve-children.d.mts.map +1 -0
- package/dist/children/resolve-children.mjs +10 -0
- package/dist/children/resolve-children.mjs.map +1 -0
- package/dist/class-name/class-name-props.d.cts +11 -0
- package/dist/class-name/class-name-props.d.cts.map +1 -0
- package/dist/class-name/class-name-props.d.mts +11 -0
- package/dist/class-name/class-name-props.d.mts.map +1 -0
- package/dist/class-name/clsx.cjs +3 -0
- package/dist/class-name/clsx.d.cts +2 -0
- package/dist/class-name/clsx.d.mts +2 -0
- package/dist/class-name/clsx.mjs +3 -0
- package/dist/class-name/just-class-name.d.cts +36 -0
- package/dist/class-name/just-class-name.d.cts.map +1 -0
- package/dist/class-name/just-class-name.d.mts +36 -0
- package/dist/class-name/just-class-name.d.mts.map +1 -0
- package/dist/class-name/resolve-class-name.cjs +12 -0
- package/dist/class-name/resolve-class-name.cjs.map +1 -0
- package/dist/class-name/resolve-class-name.d.cts +8 -0
- package/dist/class-name/resolve-class-name.d.cts.map +1 -0
- package/dist/class-name/resolve-class-name.d.mts +8 -0
- package/dist/class-name/resolve-class-name.d.mts.map +1 -0
- package/dist/class-name/resolve-class-name.mjs +10 -0
- package/dist/class-name/resolve-class-name.mjs.map +1 -0
- package/dist/color-scheme/get-prefers-color-scheme.cjs +21 -0
- package/dist/color-scheme/get-prefers-color-scheme.cjs.map +1 -0
- package/dist/color-scheme/get-prefers-color-scheme.d.cts +16 -0
- package/dist/color-scheme/get-prefers-color-scheme.d.cts.map +1 -0
- package/dist/color-scheme/get-prefers-color-scheme.d.mts +16 -0
- package/dist/color-scheme/get-prefers-color-scheme.d.mts.map +1 -0
- package/dist/color-scheme/get-prefers-color-scheme.mjs +20 -0
- package/dist/color-scheme/get-prefers-color-scheme.mjs.map +1 -0
- package/dist/color-scheme/observe-prefers-color-scheme.cjs +29 -0
- package/dist/color-scheme/observe-prefers-color-scheme.cjs.map +1 -0
- package/dist/color-scheme/observe-prefers-color-scheme.d.cts +20 -0
- package/dist/color-scheme/observe-prefers-color-scheme.d.cts.map +1 -0
- package/dist/color-scheme/observe-prefers-color-scheme.d.mts +20 -0
- package/dist/color-scheme/observe-prefers-color-scheme.d.mts.map +1 -0
- package/dist/color-scheme/observe-prefers-color-scheme.mjs +28 -0
- package/dist/color-scheme/observe-prefers-color-scheme.mjs.map +1 -0
- package/dist/index.cjs +39 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.mts +26 -0
- package/dist/index.mjs +20 -0
- package/dist/react/hooks/use-attribute.cjs +41 -0
- package/dist/react/hooks/use-attribute.cjs.map +1 -0
- package/dist/react/hooks/use-attribute.d.cts +21 -0
- package/dist/react/hooks/use-attribute.d.cts.map +1 -0
- package/dist/react/hooks/use-attribute.d.mts +21 -0
- package/dist/react/hooks/use-attribute.d.mts.map +1 -0
- package/dist/react/hooks/use-attribute.mjs +40 -0
- package/dist/react/hooks/use-attribute.mjs.map +1 -0
- package/dist/react/hooks/use-prefers-color-scheme.cjs +42 -0
- package/dist/react/hooks/use-prefers-color-scheme.cjs.map +1 -0
- package/dist/react/hooks/use-prefers-color-scheme.d.cts +29 -0
- package/dist/react/hooks/use-prefers-color-scheme.d.cts.map +1 -0
- package/dist/react/hooks/use-prefers-color-scheme.d.mts +29 -0
- package/dist/react/hooks/use-prefers-color-scheme.d.mts.map +1 -0
- package/dist/react/hooks/use-prefers-color-scheme.mjs +41 -0
- package/dist/react/hooks/use-prefers-color-scheme.mjs.map +1 -0
- package/dist/react/hooks/use-theme-by-class-name.cjs +60 -0
- package/dist/react/hooks/use-theme-by-class-name.cjs.map +1 -0
- package/dist/react/hooks/use-theme-by-class-name.d.cts +34 -0
- package/dist/react/hooks/use-theme-by-class-name.d.cts.map +1 -0
- package/dist/react/hooks/use-theme-by-class-name.d.mts +34 -0
- package/dist/react/hooks/use-theme-by-class-name.d.mts.map +1 -0
- package/dist/react/hooks/use-theme-by-class-name.mjs +59 -0
- package/dist/react/hooks/use-theme-by-class-name.mjs.map +1 -0
- package/dist/react/hooks/use-theme-by-data-attribute.cjs +73 -0
- package/dist/react/hooks/use-theme-by-data-attribute.cjs.map +1 -0
- package/dist/react/hooks/use-theme-by-data-attribute.d.cts +39 -0
- package/dist/react/hooks/use-theme-by-data-attribute.d.cts.map +1 -0
- package/dist/react/hooks/use-theme-by-data-attribute.d.mts +39 -0
- package/dist/react/hooks/use-theme-by-data-attribute.d.mts.map +1 -0
- package/dist/react/hooks/use-theme-by-data-attribute.mjs +72 -0
- package/dist/react/hooks/use-theme-by-data-attribute.mjs.map +1 -0
- package/dist/react/hooks/use-theme-by-local-storage.cjs +53 -0
- package/dist/react/hooks/use-theme-by-local-storage.cjs.map +1 -0
- package/dist/react/hooks/use-theme-by-local-storage.d.cts +37 -0
- package/dist/react/hooks/use-theme-by-local-storage.d.cts.map +1 -0
- package/dist/react/hooks/use-theme-by-local-storage.d.mts +37 -0
- package/dist/react/hooks/use-theme-by-local-storage.d.mts.map +1 -0
- package/dist/react/hooks/use-theme-by-local-storage.mjs +52 -0
- package/dist/react/hooks/use-theme-by-local-storage.mjs.map +1 -0
- package/dist/react/hooks/use-theme-stores.cjs +40 -0
- package/dist/react/hooks/use-theme-stores.cjs.map +1 -0
- package/dist/react/hooks/use-theme-stores.d.cts +38 -0
- package/dist/react/hooks/use-theme-stores.d.cts.map +1 -0
- package/dist/react/hooks/use-theme-stores.d.mts +38 -0
- package/dist/react/hooks/use-theme-stores.d.mts.map +1 -0
- package/dist/react/hooks/use-theme-stores.mjs +39 -0
- package/dist/react/hooks/use-theme-stores.mjs.map +1 -0
- package/dist/react/theme/create-theme-hook.cjs +105 -0
- package/dist/react/theme/create-theme-hook.cjs.map +1 -0
- package/dist/react/theme/create-theme-hook.d.cts +29 -0
- package/dist/react/theme/create-theme-hook.d.cts.map +1 -0
- package/dist/react/theme/create-theme-hook.d.mts +29 -0
- package/dist/react/theme/create-theme-hook.d.mts.map +1 -0
- package/dist/react/theme/create-theme-hook.mjs +104 -0
- package/dist/react/theme/create-theme-hook.mjs.map +1 -0
- package/dist/react.cjs +15 -0
- package/dist/react.d.cts +8 -0
- package/dist/react.d.mts +8 -0
- package/dist/react.mjs +9 -0
- package/dist/style/css-properties.d.cts +20 -0
- package/dist/style/css-properties.d.cts.map +1 -0
- package/dist/style/css-properties.d.mts +20 -0
- package/dist/style/css-properties.d.mts.map +1 -0
- package/dist/style/define-css-properties.cjs +25 -0
- package/dist/style/define-css-properties.cjs.map +1 -0
- package/dist/style/define-css-properties.d.cts +24 -0
- package/dist/style/define-css-properties.d.cts.map +1 -0
- package/dist/style/define-css-properties.d.mts +24 -0
- package/dist/style/define-css-properties.d.mts.map +1 -0
- package/dist/style/define-css-properties.mjs +24 -0
- package/dist/style/define-css-properties.mjs.map +1 -0
- package/dist/style/get-css-variable-value.cjs +11 -0
- package/dist/style/get-css-variable-value.cjs.map +1 -0
- package/dist/style/get-css-variable-value.d.cts +22 -0
- package/dist/style/get-css-variable-value.d.cts.map +1 -0
- package/dist/style/get-css-variable-value.d.mts +22 -0
- package/dist/style/get-css-variable-value.d.mts.map +1 -0
- package/dist/style/get-css-variable-value.mjs +10 -0
- package/dist/style/get-css-variable-value.mjs.map +1 -0
- package/dist/style/just-style.d.cts +44 -0
- package/dist/style/just-style.d.cts.map +1 -0
- package/dist/style/just-style.d.mts +44 -0
- package/dist/style/just-style.d.mts.map +1 -0
- package/dist/style/resolve-style.cjs +14 -0
- package/dist/style/resolve-style.cjs.map +1 -0
- package/dist/style/resolve-style.d.cts +11 -0
- package/dist/style/resolve-style.d.cts.map +1 -0
- package/dist/style/resolve-style.d.mts +11 -0
- package/dist/style/resolve-style.d.mts.map +1 -0
- package/dist/style/resolve-style.mjs +13 -0
- package/dist/style/resolve-style.mjs.map +1 -0
- package/dist/style/style-props.d.cts +13 -0
- package/dist/style/style-props.d.cts.map +1 -0
- package/dist/style/style-props.d.mts +13 -0
- package/dist/style/style-props.d.mts.map +1 -0
- package/dist/style/to-dom-style.cjs +33 -0
- package/dist/style/to-dom-style.cjs.map +1 -0
- package/dist/style/to-dom-style.d.cts +29 -0
- package/dist/style/to-dom-style.d.cts.map +1 -0
- package/dist/style/to-dom-style.d.mts +29 -0
- package/dist/style/to-dom-style.d.mts.map +1 -0
- package/dist/style/to-dom-style.mjs +32 -0
- package/dist/style/to-dom-style.mjs.map +1 -0
- package/dist/testing/theme/dummy-theme-store.cjs +11 -0
- package/dist/testing/theme/dummy-theme-store.cjs.map +1 -0
- package/dist/testing/theme/dummy-theme-store.mjs +10 -0
- package/dist/testing/theme/dummy-theme-store.mjs.map +1 -0
- package/dist/theme/_utils/get-theme-from-stores.cjs +24 -0
- package/dist/theme/_utils/get-theme-from-stores.cjs.map +1 -0
- package/dist/theme/_utils/get-theme-from-stores.mjs +23 -0
- package/dist/theme/_utils/get-theme-from-stores.mjs.map +1 -0
- package/dist/theme/_utils/observe-theme-from-stores.cjs +39 -0
- package/dist/theme/_utils/observe-theme-from-stores.cjs.map +1 -0
- package/dist/theme/_utils/observe-theme-from-stores.mjs +39 -0
- package/dist/theme/_utils/observe-theme-from-stores.mjs.map +1 -0
- package/dist/theme/_utils/parse-stored-theme.cjs +22 -0
- package/dist/theme/_utils/parse-stored-theme.cjs.map +1 -0
- package/dist/theme/_utils/parse-stored-theme.mjs +22 -0
- package/dist/theme/_utils/parse-stored-theme.mjs.map +1 -0
- package/dist/theme/_utils/set-theme-to-stores.cjs +16 -0
- package/dist/theme/_utils/set-theme-to-stores.cjs.map +1 -0
- package/dist/theme/_utils/set-theme-to-stores.mjs +15 -0
- package/dist/theme/_utils/set-theme-to-stores.mjs.map +1 -0
- package/dist/theme/class-name/apply-theme-to-class-name.cjs +23 -0
- package/dist/theme/class-name/apply-theme-to-class-name.cjs.map +1 -0
- package/dist/theme/class-name/apply-theme-to-class-name.mjs +22 -0
- package/dist/theme/class-name/apply-theme-to-class-name.mjs.map +1 -0
- package/dist/theme/class-name/resolve-theme-from-class-name.cjs +23 -0
- package/dist/theme/class-name/resolve-theme-from-class-name.cjs.map +1 -0
- package/dist/theme/class-name/resolve-theme-from-class-name.mjs +22 -0
- package/dist/theme/class-name/resolve-theme-from-class-name.mjs.map +1 -0
- package/dist/theme/compose-theme-stores.cjs +74 -0
- package/dist/theme/compose-theme-stores.cjs.map +1 -0
- package/dist/theme/compose-theme-stores.d.cts +33 -0
- package/dist/theme/compose-theme-stores.d.cts.map +1 -0
- package/dist/theme/compose-theme-stores.d.mts +33 -0
- package/dist/theme/compose-theme-stores.d.mts.map +1 -0
- package/dist/theme/compose-theme-stores.mjs +74 -0
- package/dist/theme/compose-theme-stores.mjs.map +1 -0
- package/dist/theme/data-attribute/apply-theme-to-data-attribute.cjs +23 -0
- package/dist/theme/data-attribute/apply-theme-to-data-attribute.cjs.map +1 -0
- package/dist/theme/data-attribute/apply-theme-to-data-attribute.mjs +22 -0
- package/dist/theme/data-attribute/apply-theme-to-data-attribute.mjs.map +1 -0
- package/dist/theme/data-attribute/resolve-theme-from-data-attribute.cjs +23 -0
- package/dist/theme/data-attribute/resolve-theme-from-data-attribute.cjs.map +1 -0
- package/dist/theme/data-attribute/resolve-theme-from-data-attribute.mjs +22 -0
- package/dist/theme/data-attribute/resolve-theme-from-data-attribute.mjs.map +1 -0
- package/dist/theme/theme-entry.cjs +13 -0
- package/dist/theme/theme-entry.cjs.map +1 -0
- package/dist/theme/theme-entry.d.cts +9 -0
- package/dist/theme/theme-entry.d.cts.map +1 -0
- package/dist/theme/theme-entry.d.mts +9 -0
- package/dist/theme/theme-entry.d.mts.map +1 -0
- package/dist/theme/theme-entry.mjs +12 -0
- package/dist/theme/theme-entry.mjs.map +1 -0
- package/dist/theme/theme-entry.types.d.cts +16 -0
- package/dist/theme/theme-entry.types.d.cts.map +1 -0
- package/dist/theme/theme-entry.types.d.mts +16 -0
- package/dist/theme/theme-entry.types.d.mts.map +1 -0
- package/dist/theme/theme-map.types.d.cts +10 -0
- package/dist/theme/theme-map.types.d.cts.map +1 -0
- package/dist/theme/theme-map.types.d.mts +10 -0
- package/dist/theme/theme-map.types.d.mts.map +1 -0
- package/dist/theme/theme-store/async-theme-store.types.d.cts +25 -0
- package/dist/theme/theme-store/async-theme-store.types.d.cts.map +1 -0
- package/dist/theme/theme-store/async-theme-store.types.d.mts +25 -0
- package/dist/theme/theme-store/async-theme-store.types.d.mts.map +1 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs +53 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts +28 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts +28 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs +53 -0
- package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.cjs +121 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.cts +65 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.mts +65 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.mjs +120 -0
- package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs +51 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts +30 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts +30 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs +51 -0
- package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs +54 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts +31 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts +31 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs +54 -0
- package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.cjs +67 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts +34 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts +34 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.mjs +67 -0
- package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.cjs +39 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.cts +32 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.mts +32 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.mjs +39 -0
- package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.cjs +67 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.cjs.map +1 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts +34 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts.map +1 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts +34 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts.map +1 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.mjs +67 -0
- package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.mjs.map +1 -0
- package/dist/theme/theme-store/theme-store-factory.types.d.cts +10 -0
- package/dist/theme/theme-store/theme-store-factory.types.d.cts.map +1 -0
- package/dist/theme/theme-store/theme-store-factory.types.d.mts +10 -0
- package/dist/theme/theme-store/theme-store-factory.types.d.mts.map +1 -0
- package/dist/theme/theme-store/theme-store.types.d.cts +33 -0
- package/dist/theme/theme-store/theme-store.types.d.cts.map +1 -0
- package/dist/theme/theme-store/theme-store.types.d.mts +33 -0
- package/dist/theme/theme-store/theme-store.types.d.mts.map +1 -0
- package/dist/theme.cjs +20 -0
- package/dist/theme.d.cts +15 -0
- package/dist/theme.d.mts +15 -0
- package/dist/theme.mjs +11 -0
- package/dist/units/get-rem-to-px-scale.cjs +30 -0
- package/dist/units/get-rem-to-px-scale.cjs.map +1 -0
- package/dist/units/get-rem-to-px-scale.d.cts +21 -0
- package/dist/units/get-rem-to-px-scale.d.cts.map +1 -0
- package/dist/units/get-rem-to-px-scale.d.mts +21 -0
- package/dist/units/get-rem-to-px-scale.d.mts.map +1 -0
- package/dist/units/get-rem-to-px-scale.mjs +29 -0
- package/dist/units/get-rem-to-px-scale.mjs.map +1 -0
- package/dist/units/px-2-num.cjs +23 -0
- package/dist/units/px-2-num.cjs.map +1 -0
- package/dist/units/px-2-num.d.cts +19 -0
- package/dist/units/px-2-num.d.cts.map +1 -0
- package/dist/units/px-2-num.d.mts +19 -0
- package/dist/units/px-2-num.d.mts.map +1 -0
- package/dist/units/px-2-num.mjs +22 -0
- package/dist/units/px-2-num.mjs.map +1 -0
- package/dist/units/px-2-rem.cjs +31 -0
- package/dist/units/px-2-rem.cjs.map +1 -0
- package/dist/units/px-2-rem.d.cts +25 -0
- package/dist/units/px-2-rem.d.cts.map +1 -0
- package/dist/units/px-2-rem.d.mts +25 -0
- package/dist/units/px-2-rem.d.mts.map +1 -0
- package/dist/units/px-2-rem.mjs +30 -0
- package/dist/units/px-2-rem.mjs.map +1 -0
- package/dist/units/rem-2-px.cjs +31 -0
- package/dist/units/rem-2-px.cjs.map +1 -0
- package/dist/units/rem-2-px.d.cts +25 -0
- package/dist/units/rem-2-px.d.cts.map +1 -0
- package/dist/units/rem-2-px.d.mts +25 -0
- package/dist/units/rem-2-px.d.mts.map +1 -0
- package/dist/units/rem-2-px.mjs +30 -0
- package/dist/units/rem-2-px.mjs.map +1 -0
- package/dist/utils/append-id.cjs +16 -0
- package/dist/utils/append-id.cjs.map +1 -0
- package/dist/utils/append-id.d.cts +12 -0
- package/dist/utils/append-id.d.cts.map +1 -0
- package/dist/utils/append-id.d.mts +12 -0
- package/dist/utils/append-id.d.mts.map +1 -0
- package/dist/utils/append-id.mjs +15 -0
- package/dist/utils/append-id.mjs.map +1 -0
- package/package.json +120 -0
- package/readme.md +15 -0
- package/src/_internal/utils/try-parse-json.ts +8 -0
- package/src/attributes/data-attribute.ts +49 -0
- package/src/attributes/get-attribute.ts +20 -0
- package/src/attributes/get-data-attribute.ts +15 -0
- package/src/attributes/observe-attribute.ts +37 -0
- package/src/attributes/observe-data-attribute.ts +29 -0
- package/src/children/just-children-fn-props.editor.default.tsx +29 -0
- package/src/children/just-children-props.editor.default.tsx +17 -0
- package/src/children/just-children.editor.default.tsx +11 -0
- package/src/children/just-children.ts +37 -0
- package/src/children/resolve-children.ts +16 -0
- package/src/class-name/class-name-props.editor.tsx +13 -0
- package/src/class-name/class-name-props.ts +7 -0
- package/src/class-name/clsx.ts +3 -0
- package/src/class-name/just-class-name-props.editor.default.tsx +23 -0
- package/src/class-name/just-class-name-resolver-state.editor.default.tsx +18 -0
- package/src/class-name/just-class-name.editor.default-class-name.tsx +28 -0
- package/src/class-name/just-class-name.editor.default.tsx +14 -0
- package/src/class-name/just-class-name.editor.type-param.tsx +25 -0
- package/src/class-name/just-class-name.ts +36 -0
- package/src/class-name/resolve-class-name.ts +12 -0
- package/src/color-scheme/get-prefers-color-scheme.ts +17 -0
- package/src/color-scheme/observe-prefers-color-scheme.ts +24 -0
- package/src/index.ts +25 -0
- package/src/react/hooks/use-attribute.ts +59 -0
- package/src/react/hooks/use-prefers-color-scheme.ts +42 -0
- package/src/react/hooks/use-theme-by-class-name.ts +69 -0
- package/src/react/hooks/use-theme-by-data-attribute.ts +84 -0
- package/src/react/hooks/use-theme-by-local-storage.ts +68 -0
- package/src/react/hooks/use-theme-stores.ts +83 -0
- package/src/react/theme/create-theme-hook.ts +197 -0
- package/src/react.ts +7 -0
- package/src/style/css-properties.ts +20 -0
- package/src/style/define-css-properties.ts +23 -0
- package/src/style/get-css-variable-value.ts +32 -0
- package/src/style/just-style-props.editor.default.tsx +17 -0
- package/src/style/just-style-resolver-state.editor.default.tsx +22 -0
- package/src/style/just-style.editor.default.tsx +17 -0
- package/src/style/just-style.editor.type-param.tsx +31 -0
- package/src/style/just-style.ts +60 -0
- package/src/style/resolve-style.ts +23 -0
- package/src/style/style-props.editor.tsx +13 -0
- package/src/style/style-props.ts +8 -0
- package/src/style/to-dom-style.ts +36 -0
- package/src/testing/button.theme.ts +21 -0
- package/src/testing/button.tsx +11 -0
- package/src/testing/log-panel.tsx +14 -0
- package/src/testing/theme/dummy-theme-store.ts +7 -0
- package/src/testing/theme/theme-result-card.tsx +43 -0
- package/src/testing/theme/theme-store-demo.tsx +87 -0
- package/src/theme/_utils/get-theme-from-stores.ts +34 -0
- package/src/theme/_utils/observe-theme-from-stores.ts +57 -0
- package/src/theme/_utils/parse-stored-theme.ts +21 -0
- package/src/theme/_utils/set-theme-to-stores.ts +23 -0
- package/src/theme/class-name/apply-theme-to-class-name.ts +26 -0
- package/src/theme/class-name/resolve-theme-from-class-name.ts +22 -0
- package/src/theme/compose-theme-stores.ts +139 -0
- package/src/theme/data-attribute/apply-theme-to-data-attribute.ts +27 -0
- package/src/theme/data-attribute/resolve-theme-from-data-attribute.ts +23 -0
- package/src/theme/theme-entry.ts +10 -0
- package/src/theme/theme-entry.types.ts +11 -0
- package/src/theme/theme-map.types.ts +6 -0
- package/src/theme/theme-store/async-theme-store.types.ts +24 -0
- package/src/theme/theme-store/class-name-theme-store/class-name-theme-store.ts +62 -0
- package/src/theme/theme-store/cookie-theme-store/cookie-theme-store.ts +174 -0
- package/src/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts +60 -0
- package/src/theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts +54 -0
- package/src/theme/theme-store/local-storage-theme-store/local-storage-theme-store.ts +83 -0
- package/src/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.ts +43 -0
- package/src/theme/theme-store/session-storage-theme-store/session-storage-theme-store.ts +83 -0
- package/src/theme/theme-store/theme-store-factory.types.ts +9 -0
- package/src/theme/theme-store/theme-store.types.ts +30 -0
- package/src/theme.ts +14 -0
- package/src/units/get-rem-to-px-scale.ts +27 -0
- package/src/units/px-2-num.ts +17 -0
- package/src/units/px-2-rem.ts +30 -0
- package/src/units/rem-2-px.ts +30 -0
- package/src/utils/append-id.ts +10 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { Required } from 'type-plus'
|
|
2
|
+
import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
|
|
3
|
+
import { parseStoredTheme } from '../../_utils/parse-stored-theme.ts'
|
|
4
|
+
import { themeEntry } from '../../theme-entry.ts'
|
|
5
|
+
import type { ThemeEntry } from '../../theme-entry.types.ts'
|
|
6
|
+
import type { ThemeMap } from '../../theme-map.types.ts'
|
|
7
|
+
import type { ThemeStore } from '../theme-store.types.ts'
|
|
8
|
+
|
|
9
|
+
export interface CookieThemeStoreOptions {
|
|
10
|
+
cookieName: string
|
|
11
|
+
path?: string | undefined
|
|
12
|
+
maxAge?: number | undefined
|
|
13
|
+
sameSite?: 'lax' | 'strict' | 'none' | undefined
|
|
14
|
+
secure?: boolean | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getCookieValue(name: string): string | null {
|
|
18
|
+
if (typeof document === 'undefined' || !document.cookie) return null
|
|
19
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
|
|
20
|
+
const value = match?.[1]
|
|
21
|
+
return value !== undefined ? decodeURIComponent(value) : null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function setCookie(
|
|
25
|
+
name: string,
|
|
26
|
+
value: string,
|
|
27
|
+
options: {
|
|
28
|
+
path?: string | undefined
|
|
29
|
+
maxAge?: number | undefined
|
|
30
|
+
sameSite?: 'lax' | 'strict' | 'none' | undefined
|
|
31
|
+
secure?: boolean | undefined
|
|
32
|
+
}
|
|
33
|
+
) {
|
|
34
|
+
const parts = [`${name}=${encodeURIComponent(value)}`]
|
|
35
|
+
parts.push(`path=${options.path ?? '/'}`)
|
|
36
|
+
if (options.maxAge !== undefined) parts.push(`max-age=${options.maxAge}`)
|
|
37
|
+
if (options.sameSite !== undefined) parts.push(`samesite=${options.sameSite}`)
|
|
38
|
+
if (options.secure) parts.push('secure')
|
|
39
|
+
// biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API has limited support; document.cookie is standard for theme persistence
|
|
40
|
+
document.cookie = parts.join('; ')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function deleteCookie(name: string, path = '/') {
|
|
44
|
+
// biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API has limited support; document.cookie is standard for theme persistence
|
|
45
|
+
document.cookie = `${name}=; path=${path}; max-age=0`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a theme store backed by cookies.
|
|
50
|
+
*
|
|
51
|
+
* Persists across sessions. Cookies are sent with every request, so the server can
|
|
52
|
+
* read the theme during SSR to avoid flash of wrong theme. Cross-tab sync is not
|
|
53
|
+
* supported (cookies have no StorageEvent).
|
|
54
|
+
*
|
|
55
|
+
* @param themes - Record mapping theme keys to values (for validation)
|
|
56
|
+
* @param options.cookieName - Cookie name for theme storage
|
|
57
|
+
* @param options.path - Cookie path (default: '/')
|
|
58
|
+
* @param options.maxAge - Cookie max-age in seconds
|
|
59
|
+
* @param options.sameSite - Cookie sameSite attribute
|
|
60
|
+
* @param options.secure - Cookie secure attribute
|
|
61
|
+
* @returns ThemeStore
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
|
|
66
|
+
* const store = cookieThemeStore(themes, { cookieName: 'theme' })
|
|
67
|
+
* store.read()
|
|
68
|
+
* store.write(themeEntry(themes, 'grayscale'))
|
|
69
|
+
* store.subscribe((themeResult) => {})
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function cookieThemeStore<Themes extends ThemeMap>(
|
|
73
|
+
themes: Themes,
|
|
74
|
+
options: CookieThemeStoreOptions
|
|
75
|
+
): Required<ThemeStore<Themes>> {
|
|
76
|
+
const { cookieName, path = '/', maxAge, sameSite, secure } = options
|
|
77
|
+
|
|
78
|
+
if (document.cookie === undefined) {
|
|
79
|
+
return dummyThemeStore
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const handlers = new Set<(theme: ThemeEntry<Themes> | undefined) => void>()
|
|
83
|
+
let lastNotifiedKey: keyof Themes | undefined = read()?.theme ?? undefined
|
|
84
|
+
|
|
85
|
+
function read() {
|
|
86
|
+
const stored = getCookieValue(cookieName)
|
|
87
|
+
const theme = parseStoredTheme(themes, stored)
|
|
88
|
+
if (theme === undefined) return undefined
|
|
89
|
+
return themeEntry(themes, theme)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function notify() {
|
|
93
|
+
const result = read()
|
|
94
|
+
const key = result?.theme ?? undefined
|
|
95
|
+
if (key === lastNotifiedKey) return
|
|
96
|
+
lastNotifiedKey = key
|
|
97
|
+
for (const h of handlers) h(result)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
read,
|
|
102
|
+
write(entry) {
|
|
103
|
+
try {
|
|
104
|
+
if (entry === undefined) {
|
|
105
|
+
deleteCookie(cookieName, path)
|
|
106
|
+
} else {
|
|
107
|
+
const opts: {
|
|
108
|
+
path: string
|
|
109
|
+
maxAge?: number
|
|
110
|
+
sameSite?: 'lax' | 'strict' | 'none'
|
|
111
|
+
secure?: boolean
|
|
112
|
+
} = { path }
|
|
113
|
+
if (maxAge !== undefined) opts.maxAge = maxAge
|
|
114
|
+
if (sameSite !== undefined) opts.sameSite = sameSite
|
|
115
|
+
if (secure) opts.secure = true
|
|
116
|
+
setCookie(cookieName, JSON.stringify(entry), opts)
|
|
117
|
+
}
|
|
118
|
+
notify()
|
|
119
|
+
} catch {
|
|
120
|
+
// Ignore quota or other errors
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
subscribe(handler) {
|
|
124
|
+
handlers.add(handler)
|
|
125
|
+
return () => {
|
|
126
|
+
handlers.delete(handler)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} satisfies ThemeStore<Themes>
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getCookieFromHeader(cookieHeader: string, name: string): string | null {
|
|
133
|
+
const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
|
|
134
|
+
const value = match?.[1]
|
|
135
|
+
return value !== undefined ? decodeURIComponent(value.trim()) : null
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Reads the theme from cookies during SSR. Use with the request's Cookie header or
|
|
140
|
+
* a framework's cookie API (e.g. Next.js cookies()).
|
|
141
|
+
*
|
|
142
|
+
* @param cookieSource - Raw Cookie header string, or a getter (name) => value for framework APIs
|
|
143
|
+
* @param themes - Record mapping theme keys to values (for validation)
|
|
144
|
+
* @param options - Optional cookie name (default: 'theme')
|
|
145
|
+
* @returns ThemeEntry if valid cookie found, otherwise undefined
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* // With raw Cookie header (Express, Remix, etc.)
|
|
150
|
+
* const theme = getThemeFromCookie(request.headers.get('Cookie') ?? '', themes)
|
|
151
|
+
*
|
|
152
|
+
* // With Next.js cookies()
|
|
153
|
+
* const theme = getThemeFromCookie(
|
|
154
|
+
* (name) => cookies().get(name)?.value ?? undefined,
|
|
155
|
+
* themes
|
|
156
|
+
* )
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function getThemeFromCookie<Themes extends ThemeMap>(
|
|
160
|
+
cookieSource: string | null | undefined | ((name: string) => string | null | undefined),
|
|
161
|
+
themes: Themes,
|
|
162
|
+
options: { cookieName?: string | undefined } = {}
|
|
163
|
+
): ThemeEntry<Themes> | undefined {
|
|
164
|
+
const cookieName = options.cookieName ?? 'theme'
|
|
165
|
+
const stored =
|
|
166
|
+
typeof cookieSource === 'function'
|
|
167
|
+
? (cookieSource(cookieName) ?? null)
|
|
168
|
+
: cookieSource
|
|
169
|
+
? getCookieFromHeader(cookieSource, cookieName)
|
|
170
|
+
: null
|
|
171
|
+
const theme = parseStoredTheme(themes, stored)
|
|
172
|
+
if (theme === undefined) return undefined
|
|
173
|
+
return themeEntry(themes, theme)
|
|
174
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Required } from 'type-plus'
|
|
2
|
+
import { getDataAttribute } from '../../../attributes/get-data-attribute.ts'
|
|
3
|
+
import { observeDataAttributes } from '../../../attributes/observe-data-attribute.ts'
|
|
4
|
+
import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
|
|
5
|
+
import { applyThemeToDataAttribute } from '../../data-attribute/apply-theme-to-data-attribute.ts'
|
|
6
|
+
import { resolveThemeFromDataAttribute } from '../../data-attribute/resolve-theme-from-data-attribute.ts'
|
|
7
|
+
import { themeEntry } from '../../theme-entry.ts'
|
|
8
|
+
import type { ThemeMap } from '../../theme-map.types.ts'
|
|
9
|
+
import type { ThemeStore } from '../theme-store.types.ts'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a theme store that reads and writes via a data attribute.
|
|
13
|
+
*
|
|
14
|
+
* @param themes - Record mapping theme keys to attribute values
|
|
15
|
+
* @param options.attributeName - Data attribute name (e.g. `data-theme`)
|
|
16
|
+
* @param options.element - Element to operate on (defaults to document.documentElement)
|
|
17
|
+
* @returns ThemeStore
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const themes = { current: 'current', grayscale: 'grayscale' }
|
|
22
|
+
* const store = dataAttributeThemeStore(themes, { attributeName: 'data-theme' })
|
|
23
|
+
* store.read() // returns themeResult from data attribute
|
|
24
|
+
* store.write(themeEntry(themes, 'grayscale'))
|
|
25
|
+
* store.subscribe((themeResult) => {})
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function dataAttributeThemeStore<Themes extends ThemeMap>(
|
|
29
|
+
themes: Themes,
|
|
30
|
+
options: { attributeName: `data-${string}`; element?: Element | undefined }
|
|
31
|
+
) {
|
|
32
|
+
const element = options.element ?? document?.documentElement
|
|
33
|
+
const { attributeName } = options
|
|
34
|
+
|
|
35
|
+
if (!element) return dummyThemeStore as Required<ThemeStore<Themes>>
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
read() {
|
|
39
|
+
const value = getDataAttribute(attributeName, element)
|
|
40
|
+
const theme = resolveThemeFromDataAttribute(themes, value)
|
|
41
|
+
if (theme === undefined) return undefined
|
|
42
|
+
return themeEntry(themes, theme)
|
|
43
|
+
},
|
|
44
|
+
write(entry) {
|
|
45
|
+
applyThemeToDataAttribute(element, attributeName, entry)
|
|
46
|
+
},
|
|
47
|
+
subscribe(handler) {
|
|
48
|
+
const observer = observeDataAttributes<string, `data-${string}`>(
|
|
49
|
+
{
|
|
50
|
+
[attributeName]: (value) => {
|
|
51
|
+
const theme = value ? resolveThemeFromDataAttribute(themes, value) : undefined
|
|
52
|
+
handler(theme ? themeEntry(themes, theme) : undefined)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
element
|
|
56
|
+
)
|
|
57
|
+
return () => observer.disconnect()
|
|
58
|
+
}
|
|
59
|
+
} satisfies ThemeStore<Themes>
|
|
60
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { themeEntry } from '../../theme-entry.ts'
|
|
2
|
+
import type { ThemeEntry } from '../../theme-entry.types.ts'
|
|
3
|
+
import type { ThemeMap } from '../../theme-map.types.ts'
|
|
4
|
+
import type { ThemeStore } from '../theme-store.types.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* In-memory theme store. Transient state; no persistence.
|
|
8
|
+
*
|
|
9
|
+
* Bakes themes at creation. Validates theme keys on write; read/write use theme keys
|
|
10
|
+
* and build ThemeEntry from the themes map, consistent with other stores.
|
|
11
|
+
*
|
|
12
|
+
* @param themes - Record mapping theme keys to values (for validation and entry construction)
|
|
13
|
+
* @returns ThemeStore
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const themes = { current: 'theme-current', grayscale: 'theme-grayscale' } as const
|
|
18
|
+
* const store = inMemoryThemeStore(themes)
|
|
19
|
+
* store.read() // undefined when empty
|
|
20
|
+
* store.write(themeEntry(themes, 'grayscale'))
|
|
21
|
+
* store.subscribe((themeResult) => {})
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function inMemoryThemeStore<Themes extends ThemeMap>(themes: Themes) {
|
|
25
|
+
let value: keyof Themes | undefined | null
|
|
26
|
+
const listeners = new Set<(v: ThemeEntry<Themes> | undefined) => void>()
|
|
27
|
+
|
|
28
|
+
function read(): ThemeEntry<Themes> | undefined {
|
|
29
|
+
if (value === undefined || value === null) return undefined
|
|
30
|
+
return themeEntry(themes, value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
read,
|
|
35
|
+
write(entry) {
|
|
36
|
+
if (entry === undefined) {
|
|
37
|
+
if (value === undefined || value === null) return
|
|
38
|
+
value = undefined
|
|
39
|
+
for (const fn of listeners) fn(undefined)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
if (!(entry.theme in themes)) return
|
|
43
|
+
if (value === entry.theme) return
|
|
44
|
+
value = entry.theme
|
|
45
|
+
for (const fn of listeners) fn(themeEntry(themes, entry.theme))
|
|
46
|
+
},
|
|
47
|
+
subscribe(handler) {
|
|
48
|
+
listeners.add(handler)
|
|
49
|
+
return () => {
|
|
50
|
+
listeners.delete(handler)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} satisfies ThemeStore<Themes>
|
|
54
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
|
|
2
|
+
import { parseStoredTheme } from '../../_utils/parse-stored-theme.ts'
|
|
3
|
+
import { themeEntry } from '../../theme-entry.ts'
|
|
4
|
+
import type { ThemeEntry } from '../../theme-entry.types.ts'
|
|
5
|
+
import type { ThemeMap } from '../../theme-map.types.ts'
|
|
6
|
+
import type { ThemeStore } from '../theme-store.types.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a theme store backed by localStorage.
|
|
10
|
+
*
|
|
11
|
+
* Persists across sessions; cross-tab sync via StorageEvent.
|
|
12
|
+
* Same-tab writes trigger manual notify (StorageEvent does not fire for same tab).
|
|
13
|
+
*
|
|
14
|
+
* @param themes - Record mapping theme keys to values (for validation)
|
|
15
|
+
* @param options.storageKey - localStorage key
|
|
16
|
+
* @returns ThemeStore
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
|
|
21
|
+
* const store = localStorageThemeStore(themes, { storageKey: 'theme' })
|
|
22
|
+
* store.read() // returns themeResult from localStorage
|
|
23
|
+
* store.write(themeEntry(themes, 'grayscale'))
|
|
24
|
+
* store.subscribe((themeResult) => {})
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function localStorageThemeStore<Themes extends ThemeMap>(
|
|
28
|
+
themes: Themes,
|
|
29
|
+
options: { storageKey: string }
|
|
30
|
+
) {
|
|
31
|
+
const { storageKey } = options
|
|
32
|
+
|
|
33
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
34
|
+
return dummyThemeStore satisfies ThemeStore<Themes>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handlers = new Set<(theme: ThemeEntry<Themes> | undefined) => void>()
|
|
38
|
+
let lastNotifiedKey: keyof Themes | undefined = read()?.theme ?? undefined
|
|
39
|
+
|
|
40
|
+
function read() {
|
|
41
|
+
const stored = window.localStorage.getItem(storageKey)
|
|
42
|
+
const theme = parseStoredTheme(themes, stored)
|
|
43
|
+
if (theme === undefined) return undefined
|
|
44
|
+
return themeEntry(themes, theme)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function notify() {
|
|
48
|
+
const result = read()
|
|
49
|
+
const key = result?.theme ?? undefined
|
|
50
|
+
if (key === lastNotifiedKey) return
|
|
51
|
+
lastNotifiedKey = key
|
|
52
|
+
for (const h of handlers) h(result)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
read,
|
|
57
|
+
write(entry) {
|
|
58
|
+
try {
|
|
59
|
+
if (entry === undefined) {
|
|
60
|
+
window.localStorage.removeItem(storageKey)
|
|
61
|
+
} else {
|
|
62
|
+
window.localStorage.setItem(storageKey, JSON.stringify(entry))
|
|
63
|
+
}
|
|
64
|
+
notify()
|
|
65
|
+
} catch {
|
|
66
|
+
// Ignore quota or other errors
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
subscribe(handler) {
|
|
70
|
+
handlers.add(handler)
|
|
71
|
+
|
|
72
|
+
const onStorage = (e: StorageEvent) => {
|
|
73
|
+
if (e.key === storageKey && e.storageArea === window.localStorage) notify()
|
|
74
|
+
}
|
|
75
|
+
window.addEventListener('storage', onStorage)
|
|
76
|
+
|
|
77
|
+
return () => {
|
|
78
|
+
handlers.delete(handler)
|
|
79
|
+
window.removeEventListener('storage', onStorage)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} satisfies ThemeStore<Themes>
|
|
83
|
+
}
|
package/src/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RequiredPick } from 'type-plus'
|
|
2
|
+
import { getPrefersColorScheme } from '../../../color-scheme/get-prefers-color-scheme.ts'
|
|
3
|
+
import { observePrefersColorScheme } from '../../../color-scheme/observe-prefers-color-scheme.ts'
|
|
4
|
+
import { themeEntry } from '../../theme-entry.ts'
|
|
5
|
+
import type { ThemeStore } from '../theme-store.types.ts'
|
|
6
|
+
|
|
7
|
+
type PrefersColorSchemeThemes = {
|
|
8
|
+
light: string | readonly string[]
|
|
9
|
+
dark: string | readonly string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a read-only theme store that reads from `prefers-color-scheme`.
|
|
14
|
+
*
|
|
15
|
+
* **Color-scheme specific:** Themes must only include `light` and `dark` keys—this store
|
|
16
|
+
* mirrors the system preference which is always one of these.
|
|
17
|
+
*
|
|
18
|
+
* Returns `ThemeEntry` for `light` or `dark` based on system preference.
|
|
19
|
+
* No write method—the value is controlled by the system.
|
|
20
|
+
*
|
|
21
|
+
* @param themes - Record with `light` and `dark` keys mapping to theme values
|
|
22
|
+
* @returns ThemeStore with read and subscribe only
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const themes = { light: 'theme-light', dark: 'theme-dark' }
|
|
27
|
+
* const store = prefersColorSchemeThemeStore(themes)
|
|
28
|
+
* store.read() // ThemeEntry for current system preference
|
|
29
|
+
* store.subscribe((entry) => {})
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function prefersColorSchemeThemeStore<Themes extends PrefersColorSchemeThemes>(
|
|
33
|
+
themes: Themes
|
|
34
|
+
): RequiredPick<ThemeStore<Themes>, 'read' | 'subscribe'> {
|
|
35
|
+
return {
|
|
36
|
+
read() {
|
|
37
|
+
return themeEntry(themes, getPrefersColorScheme())
|
|
38
|
+
},
|
|
39
|
+
subscribe(handler) {
|
|
40
|
+
return observePrefersColorScheme((scheme) => handler(themeEntry(themes, scheme)))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
|
|
2
|
+
import { parseStoredTheme } from '../../_utils/parse-stored-theme.ts'
|
|
3
|
+
import { themeEntry } from '../../theme-entry.ts'
|
|
4
|
+
import type { ThemeEntry } from '../../theme-entry.types.ts'
|
|
5
|
+
import type { ThemeMap } from '../../theme-map.types.ts'
|
|
6
|
+
import type { ThemeStore } from '../theme-store.types.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a theme store backed by sessionStorage.
|
|
10
|
+
*
|
|
11
|
+
* Persists per tab; cross-tab sync via StorageEvent when available.
|
|
12
|
+
* Same-tab writes trigger manual notify (StorageEvent does not fire for same tab).
|
|
13
|
+
*
|
|
14
|
+
* @param themes - Record mapping theme keys to values (for validation)
|
|
15
|
+
* @param options.storageKey - sessionStorage key
|
|
16
|
+
* @returns ThemeStore
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
|
|
21
|
+
* const store = sessionStorageThemeStore(themes, { storageKey: 'theme' })
|
|
22
|
+
* store.read() // returns themeResult from sessionStorage
|
|
23
|
+
* store.write(themeEntry(themes, 'grayscale'))
|
|
24
|
+
* store.subscribe((themeResult) => {})
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function sessionStorageThemeStore<Themes extends ThemeMap>(
|
|
28
|
+
themes: Themes,
|
|
29
|
+
options: { storageKey: string }
|
|
30
|
+
) {
|
|
31
|
+
const { storageKey } = options
|
|
32
|
+
|
|
33
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
34
|
+
return dummyThemeStore satisfies ThemeStore<Themes>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handlers = new Set<(theme: ThemeEntry<Themes> | undefined) => void>()
|
|
38
|
+
let lastNotifiedKey: keyof Themes | undefined = read()?.theme ?? undefined
|
|
39
|
+
|
|
40
|
+
function read() {
|
|
41
|
+
const stored = window.sessionStorage.getItem(storageKey)
|
|
42
|
+
const theme = parseStoredTheme(themes, stored)
|
|
43
|
+
if (theme === undefined) return undefined
|
|
44
|
+
return themeEntry(themes, theme)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function notify() {
|
|
48
|
+
const result = read()
|
|
49
|
+
const key = result?.theme ?? undefined
|
|
50
|
+
if (key === lastNotifiedKey) return
|
|
51
|
+
lastNotifiedKey = key
|
|
52
|
+
for (const h of handlers) h(result)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
read,
|
|
57
|
+
write(entry) {
|
|
58
|
+
try {
|
|
59
|
+
if (entry === undefined) {
|
|
60
|
+
window.sessionStorage.removeItem(storageKey)
|
|
61
|
+
} else {
|
|
62
|
+
window.sessionStorage.setItem(storageKey, JSON.stringify(entry))
|
|
63
|
+
}
|
|
64
|
+
notify()
|
|
65
|
+
} catch {
|
|
66
|
+
// Ignore quota or other errors
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
subscribe(handler) {
|
|
70
|
+
handlers.add(handler)
|
|
71
|
+
|
|
72
|
+
const onStorage = (e: StorageEvent) => {
|
|
73
|
+
if (e.key === storageKey && e.storageArea === window.sessionStorage) notify()
|
|
74
|
+
}
|
|
75
|
+
window.addEventListener('storage', onStorage)
|
|
76
|
+
|
|
77
|
+
return () => {
|
|
78
|
+
handlers.delete(handler)
|
|
79
|
+
window.removeEventListener('storage', onStorage)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} satisfies ThemeStore<Themes>
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ThemeMap } from '../theme-map.types.ts'
|
|
2
|
+
import type { AsyncThemeStore } from './async-theme-store.types.ts'
|
|
3
|
+
import type { ThemeStore } from './theme-store.types.ts'
|
|
4
|
+
|
|
5
|
+
/** Factory signature for theme stores. */
|
|
6
|
+
export type ThemeStoreFactory<Themes extends ThemeMap> = (
|
|
7
|
+
themes: Themes,
|
|
8
|
+
...args: any[]
|
|
9
|
+
) => ThemeStore<Themes> | AsyncThemeStore<Themes>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ThemeEntry } from '../theme-entry.types.ts'
|
|
2
|
+
import type { ThemeMap } from '../theme-map.types.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Interface for theme stores with optional read, write, and subscribe methods.
|
|
6
|
+
* Data flow participation is inferred from which methods exist:
|
|
7
|
+
*
|
|
8
|
+
* - **read** – Participates in waterfall read for `getThemeFromStores`
|
|
9
|
+
* - **write** – Receives writes from `setThemeToStores`
|
|
10
|
+
* - **subscribe** – GObserved for external changes via `observeThemeFromStores`
|
|
11
|
+
*
|
|
12
|
+
* All methods are optional.
|
|
13
|
+
* Built-in implementations:
|
|
14
|
+
* - `classNameThemeStore`
|
|
15
|
+
* - `cookieThemeStore`
|
|
16
|
+
* - `dataAttributeThemeStore`
|
|
17
|
+
* - `inMemoryThemeStore`
|
|
18
|
+
* - `localStorageThemeStore`
|
|
19
|
+
* - `prefersColorSchemeThemeStore`
|
|
20
|
+
* - `sessionStorageThemeStore`
|
|
21
|
+
*
|
|
22
|
+
* @typeParam Themes - Map of theme keys to their value types (string or readonly string[])
|
|
23
|
+
*/
|
|
24
|
+
export interface ThemeStore<Themes extends ThemeMap = ThemeMap> {
|
|
25
|
+
read?: (() => ThemeEntry<Themes> | undefined) | undefined
|
|
26
|
+
write?: ((entry: ThemeEntry<Themes> | undefined) => void) | undefined
|
|
27
|
+
subscribe?:
|
|
28
|
+
| ((handler: (theme: ThemeEntry<Themes> | undefined | null) => void) => () => void)
|
|
29
|
+
| undefined
|
|
30
|
+
}
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './theme/compose-theme-stores.ts'
|
|
2
|
+
export * from './theme/theme-entry.ts'
|
|
3
|
+
export type * from './theme/theme-entry.types.ts'
|
|
4
|
+
export type * from './theme/theme-map.types.ts'
|
|
5
|
+
export type * from './theme/theme-store/async-theme-store.types.ts'
|
|
6
|
+
export * from './theme/theme-store/class-name-theme-store/class-name-theme-store.ts'
|
|
7
|
+
export * from './theme/theme-store/cookie-theme-store/cookie-theme-store.ts'
|
|
8
|
+
export * from './theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts'
|
|
9
|
+
export * from './theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts'
|
|
10
|
+
export * from './theme/theme-store/local-storage-theme-store/local-storage-theme-store.ts'
|
|
11
|
+
export * from './theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.ts'
|
|
12
|
+
export * from './theme/theme-store/session-storage-theme-store/session-storage-theme-store.ts'
|
|
13
|
+
export type * from './theme/theme-store/theme-store.types.ts'
|
|
14
|
+
export type * from './theme/theme-store/theme-store-factory.types.ts'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const DEFAULT_REM_TO_PX_SCALE = 16
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the current document's rem-to-px scale (the pixel value of 1rem).
|
|
5
|
+
*
|
|
6
|
+
* Reads the computed font size of the root element (`html`), which is the value
|
|
7
|
+
* the browser uses to resolve rem units. In non-browser environments (e.g. SSR,
|
|
8
|
+
* Node), returns {@link DEFAULT_REM_TO_PX_SCALE} as a fallback.
|
|
9
|
+
*
|
|
10
|
+
* @returns The number of pixels that 1rem equals in the current document,
|
|
11
|
+
* or {@link DEFAULT_REM_TO_PX_SCALE} when not in a browser.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* getRemToPxScale() // e.g. 16 (or 20 if user increased default font size)
|
|
16
|
+
* rem2px(1, { base: getRemToPxScale() }) // matches actual 1rem in the document
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function getRemToPxScale(): number {
|
|
20
|
+
/* c8 ignore start */
|
|
21
|
+
if (typeof document === 'undefined' || !document.documentElement) {
|
|
22
|
+
return DEFAULT_REM_TO_PX_SCALE
|
|
23
|
+
}
|
|
24
|
+
/* c8 ignore end */
|
|
25
|
+
const rootFontSize = getComputedStyle(document.documentElement).fontSize
|
|
26
|
+
return Number.parseFloat(rootFontSize) ?? DEFAULT_REM_TO_PX_SCALE
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts pixel values to numbers.
|
|
3
|
+
*
|
|
4
|
+
* @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
|
|
5
|
+
* @returns The numeric value
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* px2num(16) // 16
|
|
10
|
+
* px2num('32px') // 32
|
|
11
|
+
* px2num('12.5px') // 12.5
|
|
12
|
+
* px2num('0px') // 0
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function px2num(px: number | string | undefined): number {
|
|
16
|
+
return typeof px === 'string' ? Number.parseFloat(px.replace(/px$/, '')) : Number(px)
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts pixel values to rem units.
|
|
3
|
+
*
|
|
4
|
+
* @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
|
|
5
|
+
* @param options - Optional configuration
|
|
6
|
+
* @param options.base - Base pixel value to calculate rem units from. Defaults to 16
|
|
7
|
+
* @param options.precision - Number of decimal places in the output. Defaults to 4
|
|
8
|
+
* @returns The converted value as a string with 'rem' units
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* px2rem(16) // '1.0000'
|
|
13
|
+
* px2rem('32px') // '2.0000'
|
|
14
|
+
* px2rem(20, { base: 20 }) // '1.0000'
|
|
15
|
+
* px2rem(13, { precision: 2 }) // '0.81'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function px2rem(
|
|
19
|
+
px: number | string,
|
|
20
|
+
options?: { base?: number | undefined; precision?: number | undefined }
|
|
21
|
+
): number {
|
|
22
|
+
const { base = 16, precision = 4 } = options ?? {}
|
|
23
|
+
|
|
24
|
+
if (typeof px === 'string') {
|
|
25
|
+
px = px.replace(/px$/, '')
|
|
26
|
+
px = Number.parseFloat(px)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Number((px / base).toFixed(precision))
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts rem values to pixel units.
|
|
3
|
+
*
|
|
4
|
+
* @param rem - The rem value to convert. Can be a number or string (e.g. '1rem' or '1')
|
|
5
|
+
* @param options - Optional configuration
|
|
6
|
+
* @param options.base - Base pixel value to calculate pixels from. Defaults to 16
|
|
7
|
+
* @param options.precision - Number of decimal places in the output. Defaults to 4
|
|
8
|
+
* @returns The converted value as a string with 'px' units
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* rem2px(1) // '16.0000'
|
|
13
|
+
* rem2px('2rem') // '32.0000'
|
|
14
|
+
* rem2px(1, { base: 20 }) // '20.0000'
|
|
15
|
+
* rem2px(0.8125, { precision: 2 }) // '13.00'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function rem2px(
|
|
19
|
+
rem: number | string,
|
|
20
|
+
options?: { base?: number | undefined; precision?: number | undefined }
|
|
21
|
+
): number {
|
|
22
|
+
const { base = 16, precision = 4 } = options ?? {}
|
|
23
|
+
|
|
24
|
+
if (typeof rem === 'string') {
|
|
25
|
+
rem = rem.replace(/rem$/, '')
|
|
26
|
+
rem = Number.parseFloat(rem)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Number((rem * base).toFixed(precision))
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Appends a suffix to an ID if the ID is defined.
|
|
3
|
+
*
|
|
4
|
+
* @param id - The ID to append the suffix to.
|
|
5
|
+
* @param suffix - The suffix to append to the ID.
|
|
6
|
+
* @returns The ID with the suffix appended, or undefined if the ID is undefined.
|
|
7
|
+
*/
|
|
8
|
+
export function appendId(id: string | undefined, suffix: string): string | undefined {
|
|
9
|
+
return id ? `${id}-${suffix}` : undefined
|
|
10
|
+
}
|