diff --git a/.changeset/themer-legacy-url-import.md b/.changeset/themer-legacy-url-import.md new file mode 100644 index 0000000000..2bf35b79d7 --- /dev/null +++ b/.changeset/themer-legacy-url-import.md @@ -0,0 +1,7 @@ +--- +'@sanity/themer': minor +--- + +author: @stipsan + +The themer tool imports themes from themer.sanity.build URLs. Paste a URL anywhere except a text field while the sidebar is open, or choose the new "Add from URL" in the list's menu. The tool converts the theme to `buildTheme` options, adds it to your themes and opens it in the editor. diff --git a/plugins/@sanity/themer/README.md b/plugins/@sanity/themer/README.md index 9da2f20ac5..7d40221c46 100644 --- a/plugins/@sanity/themer/README.md +++ b/plugins/@sanity/themer/README.md @@ -186,6 +186,7 @@ Try the swatch variants to preview different starting colors, or select **I'm fe - **Reorder:** drag cards, or move them from their menus. The order is saved between sessions. - **Share:** copy a theme's short code from its menu. - **Import:** paste a code into the list, or use the paste button—even in another Studio. +- **Convert a hosted theme:** paste a themer.sanity.build URL. See [Migrating from themer.sanity.build](#migrating-from-themersanitybuild). - **Remove or restore:** themes can be removed and brought back.
@@ -219,6 +220,13 @@ With `prefers-reduced-motion`, both the sidebar and split preview change layouts +const theme = buildThemeFromUrl('https://themer.sanity.build/api/hues?preset=verdant&primary=22fca8') ``` +**Want a modern `buildTheme` theme instead?** Convert it in the [Studio tool](#studio-tool): + +1. Open **Themer** and paste the hosted URL, or the whole import line, anywhere except a text field. You can also choose **Add from URL** in the list's menu. The tool adds the converted theme and opens it in the editor. +2. Back in the list, choose **Show theme code** and use the snippet in place of the URL import. + +The converted colors come close to the hosted ones, but they don't match exactly. + **Already importing from `@sanity/themer/legacy`?** Switch to `@sanity/themer-legacy`. The old import is deprecated and will be removed in `@sanity/themer@1.0`. ## License diff --git a/plugins/@sanity/themer/src/theme/options.ts b/plugins/@sanity/themer/src/theme/options.ts index ee74ebfe32..eccb7cd893 100644 --- a/plugins/@sanity/themer/src/theme/options.ts +++ b/plugins/@sanity/themer/src/theme/options.ts @@ -177,7 +177,13 @@ export function deriveTextColor(accent: string): string { }) } -function normalizeColor(value: string, name: string): string { +/** + * Validates a hex color and normalizes it to lowercase `#rrggbb`, naming the + * option it came from when it is invalid. + * + * @internal + */ +export function normalizeColor(value: string, name: string): string { if (typeof value !== 'string' || !isColor(value)) { throw new TypeError( `Invalid \`${name}\` color: ${JSON.stringify(value)} — expected a hex color like #556bfc`, diff --git a/plugins/@sanity/themer/src/tool/ImportThemeDialog.module.css b/plugins/@sanity/themer/src/tool/ImportThemeDialog.module.css new file mode 100644 index 0000000000..52af2e5390 --- /dev/null +++ b/plugins/@sanity/themer/src/tool/ImportThemeDialog.module.css @@ -0,0 +1,5 @@ +/* The converted theme's thumbnail, about as big as a theme card in the sidebar */ +.preview { + width: 160px; + max-width: 100%; +} diff --git a/plugins/@sanity/themer/src/tool/ImportThemeDialog.tsx b/plugins/@sanity/themer/src/tool/ImportThemeDialog.tsx new file mode 100644 index 0000000000..c6351a8951 --- /dev/null +++ b/plugins/@sanity/themer/src/tool/ImportThemeDialog.tsx @@ -0,0 +1,134 @@ +import {Box, Button, Card, Dialog, Flex, Stack, Text, TextArea} from '@sanity/ui' +import {useEffect, useMemo, useRef, useState} from 'react' + +import type {BuildThemeOptions} from '../theme/options' +import {convertLegacyTheme, type LegacyThemeConversion} from './legacyTheme' +import {ThemeThumbnail} from './ThemeThumbnail' + +import * as styles from './ImportThemeDialog.module.css' + +/** + * A place to paste the themer.sanity.build URL that a Studio config imports + * its theme from — or the whole import line — for touch screens and anyone + * who would not think of pasting into the sidebar. It previews the converted + * theme before importing it as one of the user's own themes. + * + * @internal + */ +export function ImportThemeDialog(props: { + onClose: () => void + onImport: (theme: {title: string; options: BuildThemeOptions}) => void +}) { + const {onClose, onImport} = props + const [text, setText] = useState('') + const textAreaRef = useRef(null) + const conversion = useMemo(() => convertLegacyTheme(text), [text]) + + // The menu the dialog opens from focuses its item again while it animates + // closed, after the field's `autoFocus` + useEffect(() => { + const frame = requestAnimationFrame(() => textAreaRef.current?.focus()) + + return () => cancelAnimationFrame(frame) + }, []) + + return ( + +