diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index f359066..eed0982 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -2,28 +2,27 @@ name: Format on: push: - branches: [main] + branches: + - '**' # Any branch jobs: format: runs-on: ubuntu-latest + # Add permissions to allow commits + permissions: + contents: write steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.4 - - - name: Setup Node.js environment - uses: actions/setup-node@v3 + - name: Checkout + uses: actions/checkout@v3 with: - node-version: 18 - - - name: Install dependencies - run: pnpm install --ignore-scripts - - - name: Format - run: pnpm run format + # Make sure the actual branch is checked out when running on pull requests + ref: ${{ github.head_ref }} + # This is important to fetch the changes to the previous commit + fetch-depth: 0 - - name: Add, Commit and Push - uses: stefanzweifel/git-auto-commit-action@v4 + - name: Prettier Action + uses: creyD/prettier_action@v4.3 with: - commit_message: 'Format' + prettier_options: '--config ./.prettierrc --ignore-path .gitignore -w "packages/**/*.{js,ts,json,css,tsx,jsx,md}" "playgrounds/**/*.{js,ts,json,css,tsx,jsx,md}"' + commit_message: "style: auto-format with action" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9bd3d96..aaa4f4a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.4 + - uses: pnpm/action-setup@v4 - name: Setup Node.js environment uses: actions/setup-node@v3 diff --git a/README.md b/README.md index db6c7b4..1167a87 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,10 @@ pnpm run dev This will start all the playground in watch mode. + +> [!NOTE] +> When code is pushed to any branch, we automatically run a prettier auto-format action that may create a new commit if the code is not properly formatted. To avoid having to pull these auto-format commits, please run `pnpm run format` locally before pushing your changes to the remote branch. + ### SolidJS The project is built using [SolidJS](https://www.solidjs.com/). If you are new to SolidJS, we recommend checking out their [documentation](https://www.solidjs.com/docs) and [tutorials](https://www.solidjs.com/tutorial). diff --git a/package.json b/package.json index e180bc4..b0e75f5 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "build": "turbo run build --filter=./packages/*", "test": "turbo run test --filter=./packages/*", "typecheck": "turbo run typecheck --filter=./packages/*", - "build-test": "turbo run build test typecheck --filter=./packages/*", + "build-test": "turbo run build test --filter=./packages/*", "format": "prettier --ignore-path .gitignore -w \"packages/**/*.{js,ts,json,css,tsx,jsx,md}\" \"playgrounds/**/*.{js,ts,json,css,tsx,jsx,md}\"", "changeset": "changeset", "version-packages": "changeset version && pnpm i", diff --git a/packages/shiki-magic-move/README.md b/packages/shiki-magic-move/README.md index 3b961dd..0e58f96 100644 --- a/packages/shiki-magic-move/README.md +++ b/packages/shiki-magic-move/README.md @@ -62,9 +62,7 @@ function animate() { :code="code" :options="{ duration: 800, stagger: 0.3, lineNumbers: true }" /> - + ``` @@ -162,17 +160,14 @@ import { ShikiMagicMovePrecompiled } from 'shiki-magic-move/vue' import { ref } from 'vue' const step = ref(1) -const compiledSteps = [/* Compiled token steps */] +const compiledSteps = [ + /* Compiled token steps */ +] ``` @@ -187,19 +182,17 @@ const shiki = await getHighlighter({ langs: ['javascript', 'typescript'], }) -const codeSteps = [ - `const hello = 'world'`, - `let hi = 'hello'`, -] +const codeSteps = [`const hello = 'world'`, `let hi = 'hello'`] const machine = createMagicMoveMachine( - code => codeToKeyedTokens(shiki, code, { - lang: 'ts', - theme: 'nord', - }), + code => + codeToKeyedTokens(shiki, code, { + lang: 'ts', + theme: 'nord', + }), { // options - } + }, ) const compiledSteps = codeSteps.map(code => machine.commit(code).current) diff --git a/packages/shiki-magic-move/build.config.ts b/packages/shiki-magic-move/build.config.ts index e00035e..628245b 100644 --- a/packages/shiki-magic-move/build.config.ts +++ b/packages/shiki-magic-move/build.config.ts @@ -34,9 +34,31 @@ export default defineBuildConfig({ }, hooks: { 'rollup:options': async (config, options) => { - options.plugins.unshift(babel({ babelHelpers: 'bundled', include: ['src/**'], exclude: ['src/solid/**', 'src/react/**'], presets: ['@babel/preset-typescript'], extensions: ['.ts', '.js'] })) - options.plugins.unshift(babel({ babelHelpers: 'bundled', include: ['src/solid/**'], presets: ['@babel/preset-typescript', 'solid'], extensions: ['.ts', '.tsx', '.js', '.jsx'] })) - options.plugins.unshift(babel({ babelHelpers: 'bundled', include: ['src/react/**'], presets: ['@babel/preset-typescript', '@babel/preset-react'], extensions: ['.ts', '.tsx', '.js', '.jsx'] })) + options.plugins.unshift( + babel({ + babelHelpers: 'bundled', + include: ['src/**'], + exclude: ['src/solid/**', 'src/react/**'], + presets: ['@babel/preset-typescript'], + extensions: ['.ts', '.js'], + }), + ) + options.plugins.unshift( + babel({ + babelHelpers: 'bundled', + include: ['src/solid/**'], + presets: ['@babel/preset-typescript', 'solid'], + extensions: ['.ts', '.tsx', '.js', '.jsx'], + }), + ) + options.plugins.unshift( + babel({ + babelHelpers: 'bundled', + include: ['src/react/**'], + presets: ['@babel/preset-typescript', '@babel/preset-react'], + extensions: ['.ts', '.tsx', '.js', '.jsx'], + }), + ) }, 'mkdist:done': async () => { await fs.writeFile('dist/svelte.mjs', 'export * from "./svelte/index.mjs"\n', 'utf-8') diff --git a/packages/shiki-magic-move/src/core.ts b/packages/shiki-magic-move/src/core.ts index f468624..5d0244f 100644 --- a/packages/shiki-magic-move/src/core.ts +++ b/packages/shiki-magic-move/src/core.ts @@ -16,13 +16,14 @@ export function createMagicMoveMachine( let previous = EMPTY let current = EMPTY - function commit(code: string, override?: MagicMoveDifferOptions): { current: KeyedTokensInfo, previous: KeyedTokensInfo } { + function commit( + code: string, + override?: MagicMoveDifferOptions, + ): { current: KeyedTokensInfo; previous: KeyedTokensInfo } { previous = current - const mergedOptions = override - ? { ...options, ...override } - : options - const newTokens = codeToKeyedTokens(code, mergedOptions.lineNumbers); - ({ from: previous, to: current } = syncTokenKeys(previous, newTokens, mergedOptions)) + const mergedOptions = override ? { ...options, ...override } : options + const newTokens = codeToKeyedTokens(code, mergedOptions.lineNumbers) + ;({ from: previous, to: current } = syncTokenKeys(previous, newTokens, mergedOptions)) return { current, previous, @@ -44,10 +45,7 @@ export function createMagicMoveMachine( } } -export function codeToKeyedTokens< - BundledLangKeys extends string, - BundledThemeKeys extends string, ->( +export function codeToKeyedTokens( highlighter: HighlighterGeneric, code: string, options: ArgumentsType['codeToTokens']>[1], @@ -84,10 +82,8 @@ export function toKeyedTokens( .flatMap((line, lineIdx): ThemedToken[] => { firstOffset = line[0]?.offset || lastOffset const lastEl = line[line.length - 1] - if (!lastEl) - lastOffset += 1 - else - lastOffset = lastEl.offset + lastEl.content.length + if (!lastEl) lastOffset += 1 + else lastOffset = lastEl.offset + lastEl.content.length const tokens = [ ...line, { @@ -120,23 +116,22 @@ export function toKeyedTokens( } function splitWhitespaceTokens(tokens: ThemedToken[][]) { - return tokens.map((line) => { - return line.flatMap((token) => { - if (token.content.match(/^\s+$/)) - return token + return tokens.map(line => { + return line.flatMap(token => { + if (token.content.match(/^\s+$/)) return token // eslint-disable-next-line regexp/no-super-linear-backtracking const match = token.content.match(/^(\s*)(.*?)(\s*)$/) - if (!match) - return token + if (!match) return token const [, leading, content, trailing] = match - if (!leading && !trailing) - return token + if (!leading && !trailing) return token - const expanded = [{ - ...token, - offset: token.offset + leading.length, - content, - }] + const expanded = [ + { + ...token, + offset: token.offset + leading.length, + content, + }, + ] if (leading) { expanded.unshift({ content: leading, @@ -159,10 +154,7 @@ function splitWhitespaceTokens(tokens: ThemedToken[][]) { * * The offsets are relative to the token, and should be sorted. */ -function splitToken( - token: KeyedToken, - offsets: number[], -): KeyedToken[] { +function splitToken(token: KeyedToken, offsets: number[]): KeyedToken[] { let lastOffset = 0 const key = token.key let index = 0 @@ -204,20 +196,19 @@ function splitToken( * Split 2D tokens array by given breakpoints. */ function splitTokens(tokens: KeyedToken[], breakpoints: number[] | Set): KeyedToken[] { - const sorted = Array.from(breakpoints instanceof Set ? breakpoints : new Set(breakpoints)) - .sort((a, b) => a - b) + const sorted = Array.from(breakpoints instanceof Set ? breakpoints : new Set(breakpoints)).sort( + (a, b) => a - b, + ) - if (!sorted.length) - return tokens + if (!sorted.length) return tokens - return tokens.flatMap((token) => { + return tokens.flatMap(token => { const breakpointsInToken = sorted .filter(i => token.offset < i && i < token.offset + token.content.length) .map(i => i - token.offset) .sort((a, b) => a - b) - if (!breakpointsInToken.length) - return token + if (!breakpointsInToken.length) return token return splitToken(token, breakpointsInToken) }) @@ -231,49 +222,52 @@ export function syncTokenKeys( from: KeyedTokensInfo, to: KeyedTokensInfo, options: MagicMoveDifferOptions = {}, -): { from: KeyedTokensInfo, to: KeyedTokensInfo } { - const { - splitTokens: shouldSplitTokens = false, - enhanceMatching = true, - } = options +): { from: KeyedTokensInfo; to: KeyedTokensInfo } { + const { splitTokens: shouldSplitTokens = false, enhanceMatching = true } = options // Run the diff and generate matches parts // In the matched parts, we override the keys with the same key so that the transition group can know they are the same element const matches = findTextMatches(from.code, to.code, options) const tokensFrom = shouldSplitTokens - ? splitTokens(from.tokens, matches.flatMap(m => m.from)) + ? splitTokens( + from.tokens, + matches.flatMap(m => m.from), + ) : from.tokens const tokensTo = shouldSplitTokens - ? splitTokens(to.tokens, matches.flatMap(m => m.to)) + ? splitTokens( + to.tokens, + matches.flatMap(m => m.to), + ) : to.tokens const matchedKeys = new Set() - matches.forEach((match) => { - const tokensF = tokensFrom.filter(t => t.offset >= match.from[0] && t.offset + t.content.length <= match.from[1]) - const tokensT = tokensTo.filter(t => t.offset >= match.to[0] && t.offset + t.content.length <= match.to[1]) + matches.forEach(match => { + const tokensF = tokensFrom.filter( + t => t.offset >= match.from[0] && t.offset + t.content.length <= match.from[1], + ) + const tokensT = tokensTo.filter( + t => t.offset >= match.to[0] && t.offset + t.content.length <= match.to[1], + ) let idxF = 0 let idxT = 0 while (idxF < tokensF.length && idxT < tokensT.length) { - if (!tokensF[idxF] || !tokensT[idxT]) - break + if (!tokensF[idxF] || !tokensT[idxT]) break if (tokensF[idxF].content === tokensT[idxT].content) { // assign the key from the first set to the second set tokensT[idxT].key = tokensF[idxF].key matchedKeys.add(tokensF[idxF].key) idxF++ idxT++ - } - else if (tokensF[idxF + 1]?.content === tokensT[idxT].content) { + } else if (tokensF[idxF + 1]?.content === tokensT[idxT].content) { // console.log('Token missing match', tokensF[idxF], undefined) idxF++ - } - else if (tokensF[idxF].content === tokensT[idxT + 1]?.content) { + } else if (tokensF[idxF].content === tokensT[idxT + 1]?.content) { // console.log('Token missing match', undefined, tokensT[idxT]) idxT++ - } - else { + } else { // console.log('Token missing match', tokensF[idxF], tokensT[idxT]) idxF++ idxT++ @@ -283,10 +277,8 @@ export function syncTokenKeys( if (enhanceMatching) { for (const token of tokensFrom) { - if (matchedKeys.has(token.key)) - continue - if (token.content.length < 3 || !token.content.match(/^[\w-]+$/)) - continue + if (matchedKeys.has(token.key)) continue + if (token.content.length < 3 || !token.content.match(/^[\w-]+$/)) continue const matched = tokensTo.find(t => t.content === token.content && !matchedKeys.has(t.key)) if (matched) { matched.key = token.key @@ -327,17 +319,14 @@ export function findTextMatches( }) aContent += text bContent += text - } - else if (op === -1) { + } else if (op === -1) { aContent += text - } - else if (op === 1) { + } else if (op === 1) { bContent += text } } - if (aContent !== a || bContent !== b) - throw new Error('Content mismatch') + if (aContent !== a || bContent !== b) throw new Error('Content mismatch') return matched } diff --git a/packages/shiki-magic-move/src/index.ts b/packages/shiki-magic-move/src/index.ts index bee6cb7..ca6167a 100644 --- a/packages/shiki-magic-move/src/index.ts +++ b/packages/shiki-magic-move/src/index.ts @@ -1,3 +1,5 @@ -(() => { - throw new Error('`shiki-magic-move` does not have main entry point. Use `shiki-magic-move/vue` for Vue or `shiki-magic-move/core` for core logics.') +;(() => { + throw new Error( + '`shiki-magic-move` does not have main entry point. Use `shiki-magic-move/vue` for Vue or `shiki-magic-move/core` for core logics.', + ) })() diff --git a/packages/shiki-magic-move/src/react/ShikiMagicMove.tsx b/packages/shiki-magic-move/src/react/ShikiMagicMove.tsx index e6fae87..7287b3a 100644 --- a/packages/shiki-magic-move/src/react/ShikiMagicMove.tsx +++ b/packages/shiki-magic-move/src/react/ShikiMagicMove.tsx @@ -18,38 +18,36 @@ export interface ShikiMagicMoveProps { export function ShikiMagicMove(props: ShikiMagicMoveProps) { const codeToTokens = React.useRef<(code: string, lineNumbers?: boolean) => KeyedTokensInfo>() - codeToTokens.current = (code, lineNumbers) => codeToKeyedTokens( - props.highlighter, - code, - { - lang: props.lang, - theme: props.theme, - }, - lineNumbers, - ) + codeToTokens.current = (code, lineNumbers) => + codeToKeyedTokens( + props.highlighter, + code, + { + lang: props.lang, + theme: props.theme, + }, + lineNumbers, + ) const machine = React.useRef>() - machine.current = machine.current || createMagicMoveMachine( - (code, lineNumbers) => codeToTokens.current!(code, lineNumbers), - ) + machine.current = + machine.current || + createMagicMoveMachine((code, lineNumbers) => codeToTokens.current!(code, lineNumbers)) const lineNumbers = props.options?.lineNumbers ?? false - const result = React.useMemo( - () => { - if ( - props.code === machine.current!.current.code - && props.theme === machine.current!.current.themeName - && props.lang === machine.current!.current.lang - && lineNumbers === machine.current!.current.lineNumbers - ) { - return machine.current! - } + const result = React.useMemo(() => { + if ( + props.code === machine.current!.current.code && + props.theme === machine.current!.current.themeName && + props.lang === machine.current!.current.lang && + lineNumbers === machine.current!.current.lineNumbers + ) { + return machine.current! + } - return machine.current!.commit(props.code, props.options) - }, - [props.code, props.options, props.theme, props.lang, lineNumbers], - ) + return machine.current!.commit(props.code, props.options) + }, [props.code, props.options, props.theme, props.lang, lineNumbers]) return ( { - const res = syncTokenKeys( - previous, - steps[Math.min(step, steps.length - 1)], - options, - ) + const res = syncTokenKeys(previous, steps[Math.min(step, steps.length - 1)], options) setPrevious(res.to) return res }, [previous, steps, step, options]) diff --git a/packages/shiki-magic-move/src/react/ShikiMagicMoveRenderer.tsx b/packages/shiki-magic-move/src/react/ShikiMagicMoveRenderer.tsx index 109becf..9b4f546 100644 --- a/packages/shiki-magic-move/src/react/ShikiMagicMoveRenderer.tsx +++ b/packages/shiki-magic-move/src/react/ShikiMagicMoveRenderer.tsx @@ -17,25 +17,22 @@ export interface ShikiMagicMoveRendererProps { /** * A wrapper component to `MagicMoveRenderer` */ -export function ShikiMagicMoveRenderer( - { - animate = true, - tokens, - previous, - options, - onStart, - onEnd, - className, - style, - }: ShikiMagicMoveRendererProps, -) { +export function ShikiMagicMoveRenderer({ + animate = true, + tokens, + previous, + options, + onStart, + onEnd, + className, + style, +}: ShikiMagicMoveRendererProps) { const container = React.useRef(null) const renderer = React.useRef() const [isMounted, setIsMounted] = React.useState(false) React.useEffect(() => { - if (!container.current) - return + if (!container.current) return // Remove previous content container.current.innerHTML = '' setIsMounted(true) @@ -44,18 +41,15 @@ export function ShikiMagicMoveRenderer( React.useEffect(() => { async function render() { - if (!renderer.current) - return + if (!renderer.current) return Object.assign(renderer.current.options, options) if (animate) { - if (previous) - renderer.current.replace(previous) + if (previous) renderer.current.replace(previous) onStart?.() await renderer.current.render(tokens) onEnd?.() - } - else { + } else { renderer.current.replace(tokens) } } @@ -73,30 +67,28 @@ export function ShikiMagicMoveRenderer( > { // Render initial tokens for SSR - // FIXME: Remove this element // Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. }
{isMounted ? undefined - : tokens.tokens.map((token) => { - if (token.content === '\n') - return
+ : tokens.tokens.map(token => { + if (token.content === '\n') return
- return ( - - {token.content} - - ) - })} + return ( + + {token.content} + + ) + })}
) diff --git a/packages/shiki-magic-move/src/react/index.ts b/packages/shiki-magic-move/src/react/index.ts index 806d941..39def42 100644 --- a/packages/shiki-magic-move/src/react/index.ts +++ b/packages/shiki-magic-move/src/react/index.ts @@ -2,8 +2,4 @@ import { ShikiMagicMove } from './ShikiMagicMove' import { ShikiMagicMovePrecompiled } from './ShikiMagicMovePrecompiled' import { ShikiMagicMoveRenderer } from './ShikiMagicMoveRenderer' -export { - ShikiMagicMove, - ShikiMagicMovePrecompiled, - ShikiMagicMoveRenderer, -} +export { ShikiMagicMove, ShikiMagicMovePrecompiled, ShikiMagicMoveRenderer } diff --git a/packages/shiki-magic-move/src/react/utils.ts b/packages/shiki-magic-move/src/react/utils.ts index 2f950eb..6c726ed 100644 --- a/packages/shiki-magic-move/src/react/utils.ts +++ b/packages/shiki-magic-move/src/react/utils.ts @@ -1,10 +1,9 @@ export function normalizeCSSProperties(css?: string | Record): React.CSSProperties { if (typeof css === 'string') { const style: Record = {} - css?.split(';').forEach((pair) => { + css?.split(';').forEach(pair => { const [key, value] = pair.split(':') - if (key && value) - style[key.trim()] = value.trim() + if (key && value) style[key.trim()] = value.trim() }) return style as React.CSSProperties } diff --git a/packages/shiki-magic-move/src/renderer.ts b/packages/shiki-magic-move/src/renderer.ts index 609e38f..8f8c679 100644 --- a/packages/shiki-magic-move/src/renderer.ts +++ b/packages/shiki-magic-move/src/renderer.ts @@ -2,12 +2,7 @@ /* eslint-disable style/brace-style */ /* eslint-disable style/indent */ /* eslint-disable style/no-tabs */ -import type { - KeyedToken, - KeyedTokensInfo, - MagicMoveElement, - MagicMoveRenderOptions, -} from './types' +import type { KeyedToken, KeyedTokensInfo, MagicMoveElement, MagicMoveRenderOptions } from './types' const CLASS_PREFIX = 'shiki-magic-move' const CLASS_LEAVE_FROM = `${CLASS_PREFIX}-leave-from` @@ -21,481 +16,440 @@ const CLASS_CONTAINER_RESIZE = `${CLASS_PREFIX}-container-resize` const CLASS_CONTAINER_RESTYLE = `${CLASS_PREFIX}-container-restyle` interface PromiseWithResolve extends Promise { - resolve: (value: T) => void + resolve: (value: T) => void } export const defaultOptions: Required = { - globalScale: 1, - duration: 500, - delayMove: 0.3, - delayLeave: 0.1, - delayEnter: 0.7, - delayContainer: 0.4, - stagger: 0, - easing: 'ease', - animateContainer: true, - containerStyle: true, - onAnimationStart: () => {}, + globalScale: 1, + duration: 500, + delayMove: 0.3, + delayLeave: 0.1, + delayEnter: 0.7, + delayContainer: 0.4, + stagger: 0, + easing: 'ease', + animateContainer: true, + containerStyle: true, + onAnimationStart: () => {}, } export class MagicMoveRenderer { - private mapDom = new Map() - private container: HTMLElement - private anchor: HTMLElement - private previousPromises: PromiseWithResolve[] = [] - - public options: Required - - private isFirstRender = true - - constructor( - target: HTMLElement | string, - options: MagicMoveRenderOptions = {}, - ) { - this.options = { - ...defaultOptions, - ...options, - } - - if (typeof target === 'string') - this.container = document.querySelector(target) as HTMLElement - else this.container = target - - // Create anchor - this.anchor = document.createElement('span') - this.anchor.style.position = 'absolute' - this.anchor.style.top = '0' - this.anchor.style.left = '0' - this.anchor.style.height = '1px' - this.anchor.style.width = '1px' - - this.container.prepend(this.anchor) - } - - private applyElementContent(el: HTMLElement, token: KeyedToken) { - if (token.content !== '\n') { - el.textContent = token.content - el.classList.add(`${CLASS_PREFIX}-item`) - } - } - - private applyElementStyle(el: HTMLElement, token: KeyedToken) { - if (token.htmlStyle) { - if (typeof token.htmlStyle === 'string') { - el.setAttribute('style', token.htmlStyle) - } else { - for (const [key, value] of Object.entries(token.htmlStyle)) { - el.style.setProperty(key, value) - } - } - } - if (token.htmlClass) - el.className = [`${CLASS_PREFIX}-item`, token.htmlClass].join(' ') - if (token.color) el.style.color = token.color - if (token.bgColor) el.style.backgroundColor = token.bgColor - } - - private applyElement(el: HTMLElement, token: KeyedToken) { - this.applyElementContent(el, token) - this.applyElementStyle(el, token) - } - - private applyNodeStyle(node: HTMLElement, step: KeyedTokensInfo) { - if (step.bg) node.style.backgroundColor = step.bg - if (step.fg) node.style.color = step.fg - if (step.rootStyle) { - const items = step.rootStyle.split(';') - for (const item of items) { - const [key, value] = item.split(':') - if (key && value) node.style.setProperty(key.trim(), value.trim()) - } - } - } - - private applyContainerStyle(step: KeyedTokensInfo) { - if (!this.options.containerStyle) return - this.applyNodeStyle(this.container, step) - } - - private registerTransitionEnd(el: HTMLElement, cb: () => void) { - return () => { - let resolved = false - let resolve = () => {} - const promise = Promise.race([ - // wait for the finish of all animation and transitions on this element then invoke the callback - Promise.allSettled( - el.getAnimations().map((animation) => animation.finished), - ).then(() => cb()), - // race it with another promise so it's still resolvable separately - new Promise((_resolve) => { - resolve = () => { - if (resolved) return - resolved = true - cb() - _resolve() - } - }), - ]) as PromiseWithResolve - promise.resolve = resolve - return promise - } - } - - setCssVariables() { - // Update CSS variables - this.container.style.setProperty( - '--smm-duration', - `${this.options.duration}ms`, - ) - this.container.style.setProperty( - '--smm-delay-move', - `${this.options.delayMove}`, - ) - this.container.style.setProperty( - '--smm-delay-leave', - `${this.options.delayLeave}`, - ) - this.container.style.setProperty( - '--smm-delay-enter', - `${this.options.delayEnter}`, - ) - this.container.style.setProperty( - '--smm-delay-container', - `${this.options.delayContainer}`, - ) - this.container.style.setProperty('--smm-easing', this.options.easing) - this.container.style.setProperty('--smm-stagger', '0') - } - - /** - * Replace tokens without animation - */ - replace(step: KeyedTokensInfo): void { - const newDomMap = new Map() - - const newChildren = step.tokens.map((token) => { - if (this.mapDom.has(token.key)) { - const el = this.mapDom.get(token.key)! - this.applyElement(el, token) - newDomMap.set(token.key, el) - this.mapDom.delete(token.key) - return el - } else { - const el = document.createElement( - token.content === '\n' ? 'br' : 'span', - ) - this.applyElement(el, token) - newDomMap.set(token.key, el) - return el - } - }) - - this.container.replaceChildren(this.anchor, ...newChildren) - - this.applyContainerStyle(step) - - this.mapDom = newDomMap - } - - // Note: This function is intentionally not async to keep the operations sync - /** - * Render tokens with animation - */ - render(step: KeyedTokensInfo): Promise { - this.setCssVariables() - - const newDomMap = new Map() - const move: HTMLElement[] = [] - const enter: HTMLElement[] = [] - const leave: HTMLElement[] = [] - const promises: (() => PromiseWithResolve)[] = [] - const magicMoveElements: MagicMoveElement[] = [] - const maxContainerDimensions = { width: 0, height: 0 } - - this.previousPromises.forEach((p) => p.resolve()) - this.previousPromises = [] - - // Callbacks to run after forced reflow - const postReflow: (() => void)[] = [] - - const { globalScale: scale } = this.options - - // Record the current position of the elements (before rerendering) - const position = new Map() - const newColor = new Map() - let anchorRect = this.anchor.getBoundingClientRect() - const containerRect = this.container.getBoundingClientRect() - for (const el of this.mapDom.values()) { - const rect = el.getBoundingClientRect() - position.set(el, { x: rect.x - anchorRect.x, y: rect.y - anchorRect.y }) - } - - const newChildren = step.tokens.map((token) => { - if (this.mapDom.has(token.key)) { - const el = this.mapDom.get(token.key)! - this.applyElementContent(el, token) - newColor.set(el, token.color || el.style.color) - postReflow.push(() => { - // Update color and style after reflow - this.applyElementStyle(el, token) - }) - move.push(el) - newDomMap.set(token.key, el) - this.mapDom.delete(token.key) - return el - } else { - const el = document.createElement( - token.content === '\n' ? 'br' : 'span', - ) - newColor.set(el, token.color || el.style.color) - this.applyElement(el, token) - enter.push(el) - newDomMap.set(token.key, el) - return el - } - }) - - for (const [_, el] of this.mapDom) { - if (el.tagName === 'BR') continue - leave.push(el) - } - - for (const el of leave) el.style.position = 'absolute' - - // Update DOM - this.container.replaceChildren(this.anchor, ...newChildren, ...leave) - - this.mapDom = newDomMap - - // Lock leave elements to their position with absolute positioning - leave.forEach((el, idx) => { - el.style.position = 'absolute' - const pos = position.get(el)! - el.style.top = `${pos.y / scale}px` - el.style.left = `${pos.x / scale}px` - - magicMoveElements.push({ - el, - x: { - start: pos.x, - end: pos.x, - }, - y: { - start: pos.y, - end: pos.y, - }, - color: { - start: el.style.color, - end: el.style.color, - }, - opacity: { - start: 1, - end: 0, - }, - }) - - if (this.options.stagger) - el.style.setProperty('--smm-stagger', `${idx * this.options.stagger}ms`) - else el.style.removeProperty('--smm-stagger') - - el.classList.add(CLASS_LEAVE_FROM) - el.classList.add(CLASS_LEAVE_ACTIVE) - - postReflow.unshift(() => { - el.classList.remove(CLASS_LEAVE_FROM) - el.classList.add(CLASS_LEAVE_TO) - }) - - promises.push( - this.registerTransitionEnd(el, () => { - el.classList.remove(CLASS_LEAVE_FROM) - el.classList.remove(CLASS_LEAVE_ACTIVE) - el.classList.remove(CLASS_ENTER_TO) - el.remove() - }), - ) - }) - - // Apply enter animation - if (!this.isFirstRender) { - enter.forEach((el, idx) => { - el.classList.add(CLASS_ENTER_FROM) - el.classList.add(CLASS_ENTER_ACTIVE) - - const elRect = el.getBoundingClientRect() - const pos = { x: elRect.x - anchorRect.x, y: elRect.y - anchorRect.y } - - magicMoveElements.push({ - el, - x: { - start: pos.x, - end: pos.x, - }, - y: { - start: pos.y, - end: pos.y, - }, - color: { - start: newColor.get(el)!, - end: newColor.get(el)!, - }, - opacity: { - start: 0, - end: 1, - }, - }) - - if (this.options.stagger) - el.style.setProperty( - '--smm-stagger', - `${idx * this.options.stagger}ms`, - ) - else el.style.removeProperty('--smm-stagger') - - postReflow.push(() => { - el.classList.remove(CLASS_ENTER_FROM) - el.classList.add(CLASS_ENTER_TO) - }) - - promises.push( - this.registerTransitionEnd(el, () => { - el.classList.remove(CLASS_ENTER_FROM) - el.classList.remove(CLASS_ENTER_ACTIVE) - el.classList.remove(CLASS_ENTER_TO) - }), - ) - }) - } - - // We recalculate the anchor position because the container might be moved - anchorRect = this.anchor.getBoundingClientRect() - // Set the position of the move elements to the old position - // Set the transition duration to 0ms to make it immediate - move.forEach((el, idx) => { - const newRect = el.getBoundingClientRect() - const newPos = { - x: newRect.x - anchorRect.x, - y: newRect.y - anchorRect.y, - } - const oldPos = position.get(el)! - el.style.transitionDuration = el.style.transitionDelay = '0ms' - const dx = (oldPos.x - newPos.x) / scale - const dy = (oldPos.y - newPos.y) / scale - el.style.transform = `translate(${dx}px, ${dy}px)` - - magicMoveElements.push({ - el, - x: { - start: oldPos.x, - end: newPos.x, - }, - y: { - start: oldPos.y, - end: newPos.y, - }, - color: { - start: el.style.color, - end: newColor.get(el)!, - }, - opacity: { - start: 1, - end: 1, - }, - }) - - if (this.options.stagger) - el.style.setProperty('--smm-stagger', `${idx * this.options.stagger}ms`) - else el.style.removeProperty('--smm-stagger') - - postReflow.unshift(() => { - // Remove transform overrides, so it will start animating back to the new position - el.classList.add(CLASS_MOVE) - el.style.transform = - el.style.transitionDuration = - el.style.transitionDelay = - '' - }) - - promises.push( - this.registerTransitionEnd(el, () => { - el.classList.remove(CLASS_MOVE) - }), - ) - }) - - // Animate the container size - if (this.options.animateContainer && !this.isFirstRender) { - const newRect = this.container.getBoundingClientRect() - - maxContainerDimensions.width = Math.max( - containerRect.width / scale, - newRect.width / scale, - ) - maxContainerDimensions.height = Math.max( - containerRect.height / scale, - newRect.height / scale, - ) - - if ( - newRect.width !== containerRect.width || - newRect.height !== containerRect.height - ) { - this.container.style.transitionDuration = - this.container.style.transitionDelay = '0ms' - this.container.style.height = `${containerRect.height / scale}px` - this.container.style.width = `${containerRect.width / scale}px` - - postReflow.unshift(() => { - this.container.classList.add(CLASS_CONTAINER_RESIZE) - this.container.style.transitionDuration = - this.container.style.transitionDelay = '' - this.container.style.height = `${newRect.height / scale}px` - this.container.style.width = `${newRect.width / scale}px` - }) - - promises.push( - this.registerTransitionEnd(this.container, () => { - this.container.classList.remove(CLASS_CONTAINER_RESIZE) - this.container.style.height = this.container.style.width = '' - }), - ) - } - } - - if (this.options.containerStyle) { - if (this.isFirstRender) { - this.applyContainerStyle(step) - } else { - postReflow.push(() => { - this.container.classList.add(CLASS_CONTAINER_RESTYLE) - this.applyContainerStyle(step) - }) - - promises.push( - this.registerTransitionEnd(this.container, () => { - this.container.classList.remove(CLASS_CONTAINER_RESTYLE) - }), - ) - } - } - - // Trigger reflow to apply the transform - forceReflow() - - postReflow.forEach((cb) => cb()) - // we need to call the functions that return the promises after the reflow - // to to allow getAnimations to have the correct transitions - const actualPromises = promises.map((promise) => promise()) - - this.options.onAnimationStart(magicMoveElements, maxContainerDimensions) - - this.isFirstRender = false - this.previousPromises = actualPromises - return Promise.all(actualPromises).then() - } + private mapDom = new Map() + private container: HTMLElement + private anchor: HTMLElement + private previousPromises: PromiseWithResolve[] = [] + + public options: Required + + private isFirstRender = true + + constructor(target: HTMLElement | string, options: MagicMoveRenderOptions = {}) { + this.options = { + ...defaultOptions, + ...options, + } + + if (typeof target === 'string') this.container = document.querySelector(target) as HTMLElement + else this.container = target + + // Create anchor + this.anchor = document.createElement('span') + this.anchor.style.position = 'absolute' + this.anchor.style.top = '0' + this.anchor.style.left = '0' + this.anchor.style.height = '1px' + this.anchor.style.width = '1px' + + this.container.prepend(this.anchor) + } + + private applyElementContent(el: HTMLElement, token: KeyedToken) { + if (token.content !== '\n') { + el.textContent = token.content + el.classList.add(`${CLASS_PREFIX}-item`) + } + } + + private applyElementStyle(el: HTMLElement, token: KeyedToken) { + if (token.htmlStyle) { + if (typeof token.htmlStyle === 'string') { + el.setAttribute('style', token.htmlStyle) + } else { + for (const [key, value] of Object.entries(token.htmlStyle)) { + el.style.setProperty(key, value) + } + } + } + if (token.htmlClass) el.className = [`${CLASS_PREFIX}-item`, token.htmlClass].join(' ') + if (token.color) el.style.color = token.color + if (token.bgColor) el.style.backgroundColor = token.bgColor + } + + private applyElement(el: HTMLElement, token: KeyedToken) { + this.applyElementContent(el, token) + this.applyElementStyle(el, token) + } + + private applyNodeStyle(node: HTMLElement, step: KeyedTokensInfo) { + if (step.bg) node.style.backgroundColor = step.bg + if (step.fg) node.style.color = step.fg + if (step.rootStyle) { + const items = step.rootStyle.split(';') + for (const item of items) { + const [key, value] = item.split(':') + if (key && value) node.style.setProperty(key.trim(), value.trim()) + } + } + } + + private applyContainerStyle(step: KeyedTokensInfo) { + if (!this.options.containerStyle) return + this.applyNodeStyle(this.container, step) + } + + private registerTransitionEnd(el: HTMLElement, cb: () => void) { + return () => { + let resolved = false + let resolve = () => {} + const promise = Promise.race([ + // wait for the finish of all animation and transitions on this element then invoke the callback + Promise.allSettled(el.getAnimations().map(animation => animation.finished)).then(() => + cb(), + ), + // race it with another promise so it's still resolvable separately + new Promise(_resolve => { + resolve = () => { + if (resolved) return + resolved = true + cb() + _resolve() + } + }), + ]) as PromiseWithResolve + promise.resolve = resolve + return promise + } + } + + setCssVariables() { + // Update CSS variables + this.container.style.setProperty('--smm-duration', `${this.options.duration}ms`) + this.container.style.setProperty('--smm-delay-move', `${this.options.delayMove}`) + this.container.style.setProperty('--smm-delay-leave', `${this.options.delayLeave}`) + this.container.style.setProperty('--smm-delay-enter', `${this.options.delayEnter}`) + this.container.style.setProperty('--smm-delay-container', `${this.options.delayContainer}`) + this.container.style.setProperty('--smm-easing', this.options.easing) + this.container.style.setProperty('--smm-stagger', '0') + } + + /** + * Replace tokens without animation + */ + replace(step: KeyedTokensInfo): void { + const newDomMap = new Map() + + const newChildren = step.tokens.map(token => { + if (this.mapDom.has(token.key)) { + const el = this.mapDom.get(token.key)! + this.applyElement(el, token) + newDomMap.set(token.key, el) + this.mapDom.delete(token.key) + return el + } else { + const el = document.createElement(token.content === '\n' ? 'br' : 'span') + this.applyElement(el, token) + newDomMap.set(token.key, el) + return el + } + }) + + this.container.replaceChildren(this.anchor, ...newChildren) + + this.applyContainerStyle(step) + + this.mapDom = newDomMap + } + + // Note: This function is intentionally not async to keep the operations sync + /** + * Render tokens with animation + */ + render(step: KeyedTokensInfo): Promise { + this.setCssVariables() + + const newDomMap = new Map() + const move: HTMLElement[] = [] + const enter: HTMLElement[] = [] + const leave: HTMLElement[] = [] + const promises: (() => PromiseWithResolve)[] = [] + const magicMoveElements: MagicMoveElement[] = [] + const maxContainerDimensions = { width: 0, height: 0 } + + this.previousPromises.forEach(p => p.resolve()) + this.previousPromises = [] + + // Callbacks to run after forced reflow + const postReflow: (() => void)[] = [] + + const { globalScale: scale } = this.options + + // Record the current position of the elements (before rerendering) + const position = new Map() + const newColor = new Map() + let anchorRect = this.anchor.getBoundingClientRect() + const containerRect = this.container.getBoundingClientRect() + for (const el of this.mapDom.values()) { + const rect = el.getBoundingClientRect() + position.set(el, { x: rect.x - anchorRect.x, y: rect.y - anchorRect.y }) + } + + const newChildren = step.tokens.map(token => { + if (this.mapDom.has(token.key)) { + const el = this.mapDom.get(token.key)! + this.applyElementContent(el, token) + newColor.set(el, token.color || el.style.color) + postReflow.push(() => { + // Update color and style after reflow + this.applyElementStyle(el, token) + }) + move.push(el) + newDomMap.set(token.key, el) + this.mapDom.delete(token.key) + return el + } else { + const el = document.createElement(token.content === '\n' ? 'br' : 'span') + newColor.set(el, token.color || el.style.color) + this.applyElement(el, token) + enter.push(el) + newDomMap.set(token.key, el) + return el + } + }) + + for (const [_, el] of this.mapDom) { + if (el.tagName === 'BR') continue + leave.push(el) + } + + for (const el of leave) el.style.position = 'absolute' + + // Update DOM + this.container.replaceChildren(this.anchor, ...newChildren, ...leave) + + this.mapDom = newDomMap + + // Lock leave elements to their position with absolute positioning + leave.forEach((el, idx) => { + el.style.position = 'absolute' + const pos = position.get(el)! + el.style.top = `${pos.y / scale}px` + el.style.left = `${pos.x / scale}px` + + magicMoveElements.push({ + el, + x: { + start: pos.x, + end: pos.x, + }, + y: { + start: pos.y, + end: pos.y, + }, + color: { + start: el.style.color, + end: el.style.color, + }, + opacity: { + start: 1, + end: 0, + }, + }) + + if (this.options.stagger) + el.style.setProperty('--smm-stagger', `${idx * this.options.stagger}ms`) + else el.style.removeProperty('--smm-stagger') + + el.classList.add(CLASS_LEAVE_FROM) + el.classList.add(CLASS_LEAVE_ACTIVE) + + postReflow.unshift(() => { + el.classList.remove(CLASS_LEAVE_FROM) + el.classList.add(CLASS_LEAVE_TO) + }) + + promises.push( + this.registerTransitionEnd(el, () => { + el.classList.remove(CLASS_LEAVE_FROM) + el.classList.remove(CLASS_LEAVE_ACTIVE) + el.classList.remove(CLASS_ENTER_TO) + el.remove() + }), + ) + }) + + // Apply enter animation + if (!this.isFirstRender) { + enter.forEach((el, idx) => { + el.classList.add(CLASS_ENTER_FROM) + el.classList.add(CLASS_ENTER_ACTIVE) + + const elRect = el.getBoundingClientRect() + const pos = { x: elRect.x - anchorRect.x, y: elRect.y - anchorRect.y } + + magicMoveElements.push({ + el, + x: { + start: pos.x, + end: pos.x, + }, + y: { + start: pos.y, + end: pos.y, + }, + color: { + start: newColor.get(el)!, + end: newColor.get(el)!, + }, + opacity: { + start: 0, + end: 1, + }, + }) + + if (this.options.stagger) + el.style.setProperty('--smm-stagger', `${idx * this.options.stagger}ms`) + else el.style.removeProperty('--smm-stagger') + + postReflow.push(() => { + el.classList.remove(CLASS_ENTER_FROM) + el.classList.add(CLASS_ENTER_TO) + }) + + promises.push( + this.registerTransitionEnd(el, () => { + el.classList.remove(CLASS_ENTER_FROM) + el.classList.remove(CLASS_ENTER_ACTIVE) + el.classList.remove(CLASS_ENTER_TO) + }), + ) + }) + } + + // We recalculate the anchor position because the container might be moved + anchorRect = this.anchor.getBoundingClientRect() + // Set the position of the move elements to the old position + // Set the transition duration to 0ms to make it immediate + move.forEach((el, idx) => { + const newRect = el.getBoundingClientRect() + const newPos = { + x: newRect.x - anchorRect.x, + y: newRect.y - anchorRect.y, + } + const oldPos = position.get(el)! + el.style.transitionDuration = el.style.transitionDelay = '0ms' + const dx = (oldPos.x - newPos.x) / scale + const dy = (oldPos.y - newPos.y) / scale + el.style.transform = `translate(${dx}px, ${dy}px)` + + magicMoveElements.push({ + el, + x: { + start: oldPos.x, + end: newPos.x, + }, + y: { + start: oldPos.y, + end: newPos.y, + }, + color: { + start: el.style.color, + end: newColor.get(el)!, + }, + opacity: { + start: 1, + end: 1, + }, + }) + + if (this.options.stagger) + el.style.setProperty('--smm-stagger', `${idx * this.options.stagger}ms`) + else el.style.removeProperty('--smm-stagger') + + postReflow.unshift(() => { + // Remove transform overrides, so it will start animating back to the new position + el.classList.add(CLASS_MOVE) + el.style.transform = el.style.transitionDuration = el.style.transitionDelay = '' + }) + + promises.push( + this.registerTransitionEnd(el, () => { + el.classList.remove(CLASS_MOVE) + }), + ) + }) + + // Animate the container size + if (this.options.animateContainer && !this.isFirstRender) { + const newRect = this.container.getBoundingClientRect() + + maxContainerDimensions.width = Math.max(containerRect.width / scale, newRect.width / scale) + maxContainerDimensions.height = Math.max(containerRect.height / scale, newRect.height / scale) + + if (newRect.width !== containerRect.width || newRect.height !== containerRect.height) { + this.container.style.transitionDuration = this.container.style.transitionDelay = '0ms' + this.container.style.height = `${containerRect.height / scale}px` + this.container.style.width = `${containerRect.width / scale}px` + + postReflow.unshift(() => { + this.container.classList.add(CLASS_CONTAINER_RESIZE) + this.container.style.transitionDuration = this.container.style.transitionDelay = '' + this.container.style.height = `${newRect.height / scale}px` + this.container.style.width = `${newRect.width / scale}px` + }) + + promises.push( + this.registerTransitionEnd(this.container, () => { + this.container.classList.remove(CLASS_CONTAINER_RESIZE) + this.container.style.height = this.container.style.width = '' + }), + ) + } + } + + if (this.options.containerStyle) { + if (this.isFirstRender) { + this.applyContainerStyle(step) + } else { + postReflow.push(() => { + this.container.classList.add(CLASS_CONTAINER_RESTYLE) + this.applyContainerStyle(step) + }) + + promises.push( + this.registerTransitionEnd(this.container, () => { + this.container.classList.remove(CLASS_CONTAINER_RESTYLE) + }), + ) + } + } + + // Trigger reflow to apply the transform + forceReflow() + + postReflow.forEach(cb => cb()) + // we need to call the functions that return the promises after the reflow + // to to allow getAnimations to have the correct transitions + const actualPromises = promises.map(promise => promise()) + + this.options.onAnimationStart(magicMoveElements, maxContainerDimensions) + + this.isFirstRender = false + this.previousPromises = actualPromises + return Promise.all(actualPromises).then() + } } // synchronously force layout to put elements into a certain state function forceReflow() { - return document.body.offsetHeight + return document.body.offsetHeight } diff --git a/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx b/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx index c784a89..7dcdb91 100644 --- a/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx +++ b/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx @@ -30,9 +30,7 @@ export function ShikiMagicMove(props: ShikiMagicMoveProps) { lineNumbers, ) - const machine = createMagicMoveMachine((code, lineNumbers) => - codeToTokens(code, lineNumbers), - ) + const machine = createMagicMoveMachine((code, lineNumbers) => codeToTokens(code, lineNumbers)) const result = createMemo(() => { const lineNumbers = props.options?.lineNumbers ?? false diff --git a/packages/shiki-magic-move/src/solid/ShikiMagicMovePrecompiled.tsx b/packages/shiki-magic-move/src/solid/ShikiMagicMovePrecompiled.tsx index 9aab124..33e4254 100644 --- a/packages/shiki-magic-move/src/solid/ShikiMagicMovePrecompiled.tsx +++ b/packages/shiki-magic-move/src/solid/ShikiMagicMovePrecompiled.tsx @@ -1,11 +1,7 @@ /** @jsxImportSource solid-js */ import { createMemo, createSignal } from 'solid-js' -import type { - KeyedTokensInfo, - MagicMoveDifferOptions, - MagicMoveRenderOptions, -} from '../types' +import type { KeyedTokensInfo, MagicMoveDifferOptions, MagicMoveRenderOptions } from '../types' import { syncTokenKeys, toKeyedTokens } from '../core' import { ShikiMagicMoveRenderer } from './ShikiMagicMoveRenderer' diff --git a/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx b/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx index 1d06e06..c1cfc35 100644 --- a/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx +++ b/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx @@ -68,7 +68,7 @@ export function ShikiMagicMoveRenderer(props: ShikiMagicMoveRendererProps) {
{isMounted() ? undefined - : props.tokens?.tokens.map((token) => { + : props.tokens?.tokens.map(token => { if (token.content === '\n') return
return ( @@ -77,9 +77,7 @@ export function ShikiMagicMoveRenderer(props: ShikiMagicMoveRendererProps) { ...normalizeCSSProperties(token.htmlStyle), color: token.color, }} - class={['shiki-magic-move-item', token.htmlClass] - .filter(Boolean) - .join(' ')} + class={['shiki-magic-move-item', token.htmlClass].filter(Boolean).join(' ')} > {token.content} diff --git a/packages/shiki-magic-move/src/solid/utils.ts b/packages/shiki-magic-move/src/solid/utils.ts index a4224b2..b2f0473 100644 --- a/packages/shiki-magic-move/src/solid/utils.ts +++ b/packages/shiki-magic-move/src/solid/utils.ts @@ -1,11 +1,9 @@ import type { JSX } from 'solid-js' -export function normalizeCSSProperties( - css?: string | Record, -): JSX.CSSProperties { +export function normalizeCSSProperties(css?: string | Record): JSX.CSSProperties { if (typeof css === 'string') { const style: Record = {} - css?.split(';').forEach((pair) => { + css?.split(';').forEach(pair => { const [key, value] = pair.split(':') if (key && value) style[key.trim()] = value.trim() }) diff --git a/packages/shiki-magic-move/src/style.css b/packages/shiki-magic-move/src/style.css index 3c005c1..5bf3b65 100644 --- a/packages/shiki-magic-move/src/style.css +++ b/packages/shiki-magic-move/src/style.css @@ -33,17 +33,23 @@ } .shiki-magic-move-move { - transition-delay: calc(calc(var(--smm-duration, 0.5s) * var(--smm-delay-move, 1)) + var(--smm-stagger, 0)); + transition-delay: calc( + calc(var(--smm-duration, 0.5s) * var(--smm-delay-move, 1)) + var(--smm-stagger, 0) + ); z-index: 1; } .shiki-magic-move-enter-active { - transition-delay: calc(calc(var(--smm-duration, 0.5s) * var(--smm-delay-enter, 1)) + var(--smm-stagger, 0)); + transition-delay: calc( + calc(var(--smm-duration, 0.5s) * var(--smm-delay-enter, 1)) + var(--smm-stagger, 0) + ); z-index: 1; } .shiki-magic-move-leave-active { - transition-delay: calc(calc(var(--smm-duration, 0.5s) * var(--smm-delay-leave, 1)) + var(--smm-stagger, 0)); + transition-delay: calc( + calc(var(--smm-duration, 0.5s) * var(--smm-delay-leave, 1)) + var(--smm-stagger, 0) + ); } .shiki-magic-move-enter-from, @@ -53,4 +59,4 @@ br.shiki-magic-move-leave-active { display: none; -} \ No newline at end of file +} diff --git a/packages/shiki-magic-move/src/types.ts b/packages/shiki-magic-move/src/types.ts index 47440e1..5b25a6b 100644 --- a/packages/shiki-magic-move/src/types.ts +++ b/packages/shiki-magic-move/src/types.ts @@ -14,7 +14,8 @@ export interface KeyedToken extends ThemedToken { htmlClass?: string } -export interface KeyedTokensInfo extends Pick { +export interface KeyedTokensInfo + extends Pick { code: string hash: string tokens: KeyedToken[] @@ -92,7 +93,10 @@ export interface MagicMoveRenderOptions { * * @default () => {} */ - onAnimationStart?: (elements: MagicMoveElement[], container: { height: number, width: number }) => void + onAnimationStart?: ( + elements: MagicMoveElement[], + container: { height: number; width: number }, + ) => void } export interface MagicMoveDifferOptions { @@ -111,7 +115,7 @@ export interface MagicMoveDifferOptions { /** * Algorithm to use to cleanup the diff */ - diffCleanup?: ((diffs: Diff[]) => Diff[] | void) + diffCleanup?: (diffs: Diff[]) => Diff[] | void /** * Enhance the matching algorithm to match tokens that has same content */ diff --git a/packages/shiki-magic-move/src/vue/ShikiMagicMove.ts b/packages/shiki-magic-move/src/vue/ShikiMagicMove.ts index 133f90b..9090893 100644 --- a/packages/shiki-magic-move/src/vue/ShikiMagicMove.ts +++ b/packages/shiki-magic-move/src/vue/ShikiMagicMove.ts @@ -29,32 +29,31 @@ export const ShikiMagicMove = /* #__PURE__ */ defineComponent({ default: () => ({}), }, }, - emits: [ - 'start', - 'end', - ], + emits: ['start', 'end'], setup(props, { emit }) { const machine = createMagicMoveMachine( - code => codeToKeyedTokens( - props.highlighter, - code, - { - lang: props.lang, - theme: props.theme, - }, - props.options.lineNumbers, - ), + code => + codeToKeyedTokens( + props.highlighter, + code, + { + lang: props.lang, + theme: props.theme, + }, + props.options.lineNumbers, + ), props.options, ) const result = computed(() => machine.commit(props.code)) - return () => h(ShikiMagicMoveRenderer, { - tokens: result.value.current, - options: props.options, - previous: result.value.previous, - onStart: () => emit('start'), - onEnd: () => emit('end'), - }) + return () => + h(ShikiMagicMoveRenderer, { + tokens: result.value.current, + options: props.options, + previous: result.value.previous, + onStart: () => emit('start'), + onEnd: () => emit('end'), + }) }, }) diff --git a/packages/shiki-magic-move/src/vue/ShikiMagicMovePrecompiled.ts b/packages/shiki-magic-move/src/vue/ShikiMagicMovePrecompiled.ts index 3d2cf81..3a09286 100644 --- a/packages/shiki-magic-move/src/vue/ShikiMagicMovePrecompiled.ts +++ b/packages/shiki-magic-move/src/vue/ShikiMagicMovePrecompiled.ts @@ -28,10 +28,7 @@ export const ShikiMagicMovePrecompiled = /* #__PURE__ */ defineComponent({ default: () => ({}), }, }, - emits: [ - 'start', - 'end', - ], + emits: ['start', 'end'], setup(props, { emit }) { const EMPTY = toKeyedTokens('', []) @@ -46,13 +43,14 @@ export const ShikiMagicMovePrecompiled = /* #__PURE__ */ defineComponent({ return res }) - return () => h(ShikiMagicMoveRenderer, { - tokens: result.value.to, - previous: result.value.from, - options: props.options, - animate: props.animate, - onStart: () => emit('start'), - onEnd: () => emit('end'), - }) + return () => + h(ShikiMagicMoveRenderer, { + tokens: result.value.to, + previous: result.value.from, + options: props.options, + animate: props.animate, + onStart: () => emit('start'), + onEnd: () => emit('end'), + }) }, }) diff --git a/packages/shiki-magic-move/src/vue/ShikiMagicMoveRenderer.ts b/packages/shiki-magic-move/src/vue/ShikiMagicMoveRenderer.ts index ef56f26..258f5d5 100644 --- a/packages/shiki-magic-move/src/vue/ShikiMagicMoveRenderer.ts +++ b/packages/shiki-magic-move/src/vue/ShikiMagicMoveRenderer.ts @@ -25,10 +25,7 @@ export const ShikiMagicMoveRenderer = /* #__PURE__ */ defineComponent({ type: Object as PropType, }, }, - emits: [ - 'end', - 'start', - ], + emits: ['end', 'start'], setup(props, { emit }) { const container = ref() @@ -42,19 +39,17 @@ export const ShikiMagicMoveRenderer = /* #__PURE__ */ defineComponent({ watch( () => props.tokens, - async (tokens) => { + async tokens => { Object.assign(renderer.options, props.options) if (props.animate) { - if (props.previous) - renderer.replace(props.previous) + if (props.previous) renderer.replace(props.previous) await nextTick() const process = renderer.render(tokens) await nextTick() emit('start') await process emit('end') - } - else { + } else { renderer.replace(tokens) } }, @@ -62,25 +57,25 @@ export const ShikiMagicMoveRenderer = /* #__PURE__ */ defineComponent({ ) }) - return () => h( - 'pre', - { ref: container, class: 'shiki-magic-move-container' }, - // Render initial tokens for SSR - isMounted - ? undefined - : renderList(props.tokens.tokens, (token) => { - if (token.content === '\n') - return h('br', { key: token.key }) - return h( - 'span', - { - style: [{ color: token.color }, token.htmlStyle], - class: ['shiki-magic-move-item', token.htmlClass], - key: token.key, - }, - token.content, - ) - }), - ) + return () => + h( + 'pre', + { ref: container, class: 'shiki-magic-move-container' }, + // Render initial tokens for SSR + isMounted + ? undefined + : renderList(props.tokens.tokens, token => { + if (token.content === '\n') return h('br', { key: token.key }) + return h( + 'span', + { + style: [{ color: token.color }, token.htmlStyle], + class: ['shiki-magic-move-item', token.htmlClass], + key: token.key, + }, + token.content, + ) + }), + ) }, }) diff --git a/packages/shiki-magic-move/src/vue/index.ts b/packages/shiki-magic-move/src/vue/index.ts index f4e7998..93f064b 100644 --- a/packages/shiki-magic-move/src/vue/index.ts +++ b/packages/shiki-magic-move/src/vue/index.ts @@ -3,11 +3,7 @@ import { ShikiMagicMove } from './ShikiMagicMove' import { ShikiMagicMovePrecompiled } from './ShikiMagicMovePrecompiled' import { ShikiMagicMoveRenderer } from './ShikiMagicMoveRenderer' -export { - ShikiMagicMove, - ShikiMagicMovePrecompiled, - ShikiMagicMoveRenderer, -} +export { ShikiMagicMove, ShikiMagicMovePrecompiled, ShikiMagicMoveRenderer } export function install(app: App) { app.component('ShikiMagicMove', ShikiMagicMove) diff --git a/packages/shiki-magic-move/test/diff.test.ts b/packages/shiki-magic-move/test/diff.test.ts index 9b7df10..6d7ec1f 100644 --- a/packages/shiki-magic-move/test/diff.test.ts +++ b/packages/shiki-magic-move/test/diff.test.ts @@ -242,9 +242,9 @@ it('diff3 enhanceMatching', async () => { enhanceMatching: true, }).to - expect(printDiff(tokens2WithoutEnhance, originKeys2)) - .not - .toEqual(printDiff(tokens2WithEnhance, originKeys2)) + expect(printDiff(tokens2WithoutEnhance, originKeys2)).not.toEqual( + printDiff(tokens2WithEnhance, originKeys2), + ) expect(printDiff(tokens2WithEnhance, originKeys2)).toMatchInlineSnapshot(` " @@ -328,7 +328,7 @@ function printDiff(info: KeyedTokensInfo, keys: string[]) { } function normalizeKeys(info: KeyedTokensInfo, name: string) { - info.tokens.forEach((t) => { + info.tokens.forEach(t => { t.key = t.key.replace(info.hash, name) }) } diff --git a/playgrounds/app/migrations/meta/0000_snapshot.json b/playgrounds/app/migrations/meta/0000_snapshot.json index 45a7f52..03654f7 100644 --- a/playgrounds/app/migrations/meta/0000_snapshot.json +++ b/playgrounds/app/migrations/meta/0000_snapshot.json @@ -123,30 +123,22 @@ "indexes": { "users_table_email_unique": { "name": "users_table_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true }, "users_table_githubId_unique": { "name": "users_table_githubId_unique", - "columns": [ - "githubId" - ], + "columns": ["githubId"], "isUnique": true }, "users_table_githubUsername_unique": { "name": "users_table_githubUsername_unique", - "columns": [ - "githubUsername" - ], + "columns": ["githubUsername"], "isUnique": true }, "users_table_githubAvatarUrl_unique": { "name": "users_table_githubAvatarUrl_unique", - "columns": [ - "githubAvatarUrl" - ], + "columns": ["githubAvatarUrl"], "isUnique": true } }, @@ -166,4 +158,4 @@ "internal": { "indexes": {} } -} \ No newline at end of file +} diff --git a/playgrounds/app/migrations/meta/0001_snapshot.json b/playgrounds/app/migrations/meta/0001_snapshot.json index 6f42f10..e85f6b9 100644 --- a/playgrounds/app/migrations/meta/0001_snapshot.json +++ b/playgrounds/app/migrations/meta/0001_snapshot.json @@ -153,12 +153,8 @@ "name": "snippets_table_userId_users_table_id_fk", "tableFrom": "snippets_table", "tableTo": "users_table", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -225,30 +221,22 @@ "indexes": { "users_table_email_unique": { "name": "users_table_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true }, "users_table_githubId_unique": { "name": "users_table_githubId_unique", - "columns": [ - "githubId" - ], + "columns": ["githubId"], "isUnique": true }, "users_table_githubUsername_unique": { "name": "users_table_githubUsername_unique", - "columns": [ - "githubUsername" - ], + "columns": ["githubUsername"], "isUnique": true }, "users_table_githubAvatarUrl_unique": { "name": "users_table_githubAvatarUrl_unique", - "columns": [ - "githubAvatarUrl" - ], + "columns": ["githubAvatarUrl"], "isUnique": true } }, @@ -268,4 +256,4 @@ "internal": { "indexes": {} } -} \ No newline at end of file +} diff --git a/playgrounds/app/migrations/meta/0002_snapshot.json b/playgrounds/app/migrations/meta/0002_snapshot.json index a30d2bd..d6fe8b0 100644 --- a/playgrounds/app/migrations/meta/0002_snapshot.json +++ b/playgrounds/app/migrations/meta/0002_snapshot.json @@ -153,12 +153,8 @@ "name": "snippets_table_userId_users_table_id_fk", "tableFrom": "snippets_table", "tableTo": "users_table", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -225,30 +221,22 @@ "indexes": { "users_table_email_unique": { "name": "users_table_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true }, "users_table_githubId_unique": { "name": "users_table_githubId_unique", - "columns": [ - "githubId" - ], + "columns": ["githubId"], "isUnique": true }, "users_table_githubUsername_unique": { "name": "users_table_githubUsername_unique", - "columns": [ - "githubUsername" - ], + "columns": ["githubUsername"], "isUnique": true }, "users_table_githubAvatarUrl_unique": { "name": "users_table_githubAvatarUrl_unique", - "columns": [ - "githubAvatarUrl" - ], + "columns": ["githubAvatarUrl"], "isUnique": true } }, @@ -268,4 +256,4 @@ "internal": { "indexes": {} } -} \ No newline at end of file +} diff --git a/playgrounds/app/migrations/meta/0003_snapshot.json b/playgrounds/app/migrations/meta/0003_snapshot.json index dee187a..dca9fcd 100644 --- a/playgrounds/app/migrations/meta/0003_snapshot.json +++ b/playgrounds/app/migrations/meta/0003_snapshot.json @@ -169,12 +169,8 @@ "name": "snippets_table_userId_users_table_id_fk", "tableFrom": "snippets_table", "tableTo": "users_table", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -241,30 +237,22 @@ "indexes": { "users_table_email_unique": { "name": "users_table_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true }, "users_table_githubId_unique": { "name": "users_table_githubId_unique", - "columns": [ - "githubId" - ], + "columns": ["githubId"], "isUnique": true }, "users_table_githubUsername_unique": { "name": "users_table_githubUsername_unique", - "columns": [ - "githubUsername" - ], + "columns": ["githubUsername"], "isUnique": true }, "users_table_githubAvatarUrl_unique": { "name": "users_table_githubAvatarUrl_unique", - "columns": [ - "githubAvatarUrl" - ], + "columns": ["githubAvatarUrl"], "isUnique": true } }, @@ -284,4 +272,4 @@ "internal": { "indexes": {} } -} \ No newline at end of file +} diff --git a/playgrounds/app/migrations/meta/0004_snapshot.json b/playgrounds/app/migrations/meta/0004_snapshot.json index 5878655..3a8c22b 100644 --- a/playgrounds/app/migrations/meta/0004_snapshot.json +++ b/playgrounds/app/migrations/meta/0004_snapshot.json @@ -201,12 +201,8 @@ "name": "snippets_table_userId_users_table_id_fk", "tableFrom": "snippets_table", "tableTo": "users_table", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -273,30 +269,22 @@ "indexes": { "users_table_email_unique": { "name": "users_table_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true }, "users_table_githubId_unique": { "name": "users_table_githubId_unique", - "columns": [ - "githubId" - ], + "columns": ["githubId"], "isUnique": true }, "users_table_githubUsername_unique": { "name": "users_table_githubUsername_unique", - "columns": [ - "githubUsername" - ], + "columns": ["githubUsername"], "isUnique": true }, "users_table_githubAvatarUrl_unique": { "name": "users_table_githubAvatarUrl_unique", - "columns": [ - "githubAvatarUrl" - ], + "columns": ["githubAvatarUrl"], "isUnique": true } }, @@ -316,4 +304,4 @@ "internal": { "indexes": {} } -} \ No newline at end of file +} diff --git a/playgrounds/app/migrations/meta/_journal.json b/playgrounds/app/migrations/meta/_journal.json index 95663f9..3da9b90 100644 --- a/playgrounds/app/migrations/meta/_journal.json +++ b/playgrounds/app/migrations/meta/_journal.json @@ -38,4 +38,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/playgrounds/app/src/app.css b/playgrounds/app/src/app.css index 753dd98..1c81166 100644 --- a/playgrounds/app/src/app.css +++ b/playgrounds/app/src/app.css @@ -49,7 +49,7 @@ } .dark, - [data-kb-theme="dark"] { + [data-kb-theme='dark'] { --background: 240 10% 3.9%; --foreground: 0 0% 98%; @@ -102,8 +102,8 @@ body { @apply bg-background text-foreground; font-feature-settings: - "rlig" 1, - "calt" 1; + 'rlig' 1, + 'calt' 1; } } @@ -140,7 +140,6 @@ display: none; } - /* Full App Height and Letting Footer in the End Max Width to the Main Container for Larger Screen Sizes @@ -148,6 +147,6 @@ #app { @apply min-h-screen flex flex-col; } -#app main { - @apply w-full flex-1 max-w-screen-2xl mx-auto; -} \ No newline at end of file +#app main { + @apply w-full flex-1 max-w-screen-2xl mx-auto; +} diff --git a/playgrounds/app/src/components/ui/button.tsx b/playgrounds/app/src/components/ui/button.tsx index 93f0430..767fdb2 100644 --- a/playgrounds/app/src/components/ui/button.tsx +++ b/playgrounds/app/src/components/ui/button.tsx @@ -1,46 +1,46 @@ -import type { JSX, ValidComponent } from "solid-js" -import { splitProps } from "solid-js" +import type { JSX, ValidComponent } from 'solid-js' +import { splitProps } from 'solid-js' -import * as ButtonPrimitive from "@kobalte/core/button" -import type { PolymorphicProps } from "@kobalte/core/polymorphic" -import type { VariantProps } from "class-variance-authority" -import { cva } from "class-variance-authority" +import * as ButtonPrimitive from '@kobalte/core/button' +import type { PolymorphicProps } from '@kobalte/core/polymorphic' +import type { VariantProps } from 'class-variance-authority' +import { cva } from 'class-variance-authority' -import { cn } from "~/lib/utils" +import { cn } from '~/lib/utils' const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', { variants: { variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: "border border-input hover:bg-accent hover:text-accent-foreground", - secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline" + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: 'border border-input hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', }, size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "size-10" - } + default: 'h-10 px-4 py-2', + sm: 'h-9 rounded-md px-3', + lg: 'h-11 rounded-md px-8', + icon: 'size-10', + }, }, defaultVariants: { - variant: "default", - size: "default" - } - } + variant: 'default', + size: 'default', + }, + }, ) -type ButtonProps = ButtonPrimitive.ButtonRootProps & +type ButtonProps = ButtonPrimitive.ButtonRootProps & VariantProps & { class?: string | undefined; children?: JSX.Element } -const Button = ( - props: PolymorphicProps> +const Button = ( + props: PolymorphicProps>, ) => { - const [local, others] = splitProps(props as ButtonProps, ["variant", "size", "class"]) + const [local, others] = splitProps(props as ButtonProps, ['variant', 'size', 'class']) return ( > = (props) => { - const [local, others] = splitProps(props, ["class"]) +const Card: Component> = props => { + const [local, others] = splitProps(props, ['class']) return (
) } -const CardHeader: Component> = (props) => { - const [local, others] = splitProps(props, ["class"]) - return
+const CardHeader: Component> = props => { + const [local, others] = splitProps(props, ['class']) + return
} -const CardTitle: Component> = (props) => { - const [local, others] = splitProps(props, ["class"]) +const CardTitle: Component> = props => { + const [local, others] = splitProps(props, ['class']) return ( -

+

) } -const CardDescription: Component> = (props) => { - const [local, others] = splitProps(props, ["class"]) - return

+const CardDescription: Component> = props => { + const [local, others] = splitProps(props, ['class']) + return

} -const CardContent: Component> = (props) => { - const [local, others] = splitProps(props, ["class"]) - return

+const CardContent: Component> = props => { + const [local, others] = splitProps(props, ['class']) + return
} -const CardFooter: Component> = (props) => { - const [local, others] = splitProps(props, ["class"]) - return
+const CardFooter: Component> = props => { + const [local, others] = splitProps(props, ['class']) + return
} export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/playgrounds/app/src/components/ui/dialog.tsx b/playgrounds/app/src/components/ui/dialog.tsx index 96981fc..82e945e 100644 --- a/playgrounds/app/src/components/ui/dialog.tsx +++ b/playgrounds/app/src/components/ui/dialog.tsx @@ -1,16 +1,16 @@ -import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" -import { splitProps } from "solid-js" +import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js' +import { splitProps } from 'solid-js' -import * as DialogPrimitive from "@kobalte/core/dialog" -import type { PolymorphicProps } from "@kobalte/core/polymorphic" +import * as DialogPrimitive from '@kobalte/core/dialog' +import type { PolymorphicProps } from '@kobalte/core/polymorphic' -import { cn } from "~/lib/utils" +import { cn } from '~/lib/utils' const Dialog = DialogPrimitive.Root const DialogTrigger = DialogPrimitive.Trigger -const DialogPortal: Component = (props) => { - const [, rest] = splitProps(props, ["children"]) +const DialogPortal: Component = props => { + const [, rest] = splitProps(props, ['children']) return (
@@ -20,41 +20,41 @@ const DialogPortal: Component = (props) => { ) } -type DialogOverlayProps = +type DialogOverlayProps = DialogPrimitive.DialogOverlayProps & { class?: string | undefined } -const DialogOverlay = ( - props: PolymorphicProps> +const DialogOverlay = ( + props: PolymorphicProps>, ) => { - const [, rest] = splitProps(props as DialogOverlayProps, ["class"]) + const [, rest] = splitProps(props as DialogOverlayProps, ['class']) return ( ) } -type DialogContentProps = +type DialogContentProps = DialogPrimitive.DialogContentProps & { class?: string | undefined children?: JSX.Element } -const DialogContent = ( - props: PolymorphicProps> +const DialogContent = ( + props: PolymorphicProps>, ) => { - const [, rest] = splitProps(props as DialogContentProps, ["class", "children"]) + const [, rest] = splitProps(props as DialogContentProps, ['class', 'children']) return ( @@ -80,51 +80,51 @@ const DialogContent = ( ) } -const DialogHeader: Component> = (props) => { - const [, rest] = splitProps(props, ["class"]) +const DialogHeader: Component> = props => { + const [, rest] = splitProps(props, ['class']) return ( -
+
) } -const DialogFooter: Component> = (props) => { - const [, rest] = splitProps(props, ["class"]) +const DialogFooter: Component> = props => { + const [, rest] = splitProps(props, ['class']) return (
) } -type DialogTitleProps = DialogPrimitive.DialogTitleProps & { +type DialogTitleProps = DialogPrimitive.DialogTitleProps & { class?: string | undefined } -const DialogTitle = ( - props: PolymorphicProps> +const DialogTitle = ( + props: PolymorphicProps>, ) => { - const [, rest] = splitProps(props as DialogTitleProps, ["class"]) + const [, rest] = splitProps(props as DialogTitleProps, ['class']) return ( ) } -type DialogDescriptionProps = +type DialogDescriptionProps = DialogPrimitive.DialogDescriptionProps & { class?: string | undefined } -const DialogDescription = ( - props: PolymorphicProps> +const DialogDescription = ( + props: PolymorphicProps>, ) => { - const [, rest] = splitProps(props as DialogDescriptionProps, ["class"]) + const [, rest] = splitProps(props as DialogDescriptionProps, ['class']) return ( ) @@ -137,5 +137,5 @@ export { DialogHeader, DialogFooter, DialogTitle, - DialogDescription + DialogDescription, } diff --git a/playgrounds/app/src/components/ui/label.tsx b/playgrounds/app/src/components/ui/label.tsx index 0a8db1d..5cd8ca0 100644 --- a/playgrounds/app/src/components/ui/label.tsx +++ b/playgrounds/app/src/components/ui/label.tsx @@ -1,15 +1,15 @@ -import type { Component, ComponentProps } from "solid-js" -import { splitProps } from "solid-js" +import type { Component, ComponentProps } from 'solid-js' +import { splitProps } from 'solid-js' -import { cn } from "~/lib/utils" +import { cn } from '~/lib/utils' -const Label: Component> = (props) => { - const [local, others] = splitProps(props, ["class"]) +const Label: Component> = props => { + const [local, others] = splitProps(props, ['class']) return (