Skip to content

Commit

Permalink
fix(studio): display escape hatch values in textStyles (#2452)
Browse files Browse the repository at this point in the history
* fix: logo height

* chore: add missing export

* chore: typings

* feat(token-dictionary): deepResolveReference

* fix: studio hmr

* fix: handle escape hatch syntax in studio textStyles page

* fix: add EmptyState to tokens page

* chore: add changeset
  • Loading branch information
astahmer authored Apr 2, 2024
1 parent f89db71 commit 93dc9f5
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 58 deletions.
18 changes: 18 additions & 0 deletions .changeset/hot-rivers-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@pandacss/token-dictionary': patch
'@pandacss/studio': patch
---

Public changes: Some quality of life fixes for the Studio:

- Handle displaying values using the `[xxx]` escape-hatch syntax for `textStyles` in the studio
- Display an empty state when there's no token in a specific token page in the studio

---

(mostly) Internal changes:

- Add `deepResolveReference` in TokenDictionary, helpful to get the raw value from a semantic token by recursively
traversing the token references.
- Added some exports in the `@pandacss/token-dictionary` package, mostly useful when building tooling around Panda
(Prettier/ESLint/VSCode plugin etc)
3 changes: 1 addition & 2 deletions packages/studio/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import react from '@astrojs/react'
import studio from '@pandacss/astro-plugin-studio'
import { defineConfig } from 'astro/config'

// https://astro.build/config
export default defineConfig({
devToolbar: { enabled: true },
integrations: [react(), studio()],
integrations: [studio()],
})
8 changes: 4 additions & 4 deletions packages/studio/src/components/colors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ export default function Colors() {
<SemanticColorDisplay
value={colors.base.value}
condition="base"
token={getColorFromReference(colors.extensions.conditions.base)}
token={getColorFromReference(colors.extensions.conditions!.base)}
/>
<SemanticColorDisplay
value={colors[colors.extensions.condition].value}
condition={colors.extensions.condition}
token={getColorFromReference(colors.extensions.conditions[colors.extensions.condition])}
value={colors[colors.extensions.condition!].value}
condition={colors.extensions.condition!}
token={getColorFromReference(colors.extensions.conditions![colors.extensions.condition!])}
/>
</HStack>

Expand Down
10 changes: 10 additions & 0 deletions packages/studio/src/components/font-family.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react'
import { Flex, HStack, Square, Stack, panda } from '../../styled-system/jsx'
import * as context from '../lib/panda-context'
import { EmptyState } from './empty-state'
import { TypographyIcon } from './icons'

const fonts = context.getTokens('fonts')

Expand All @@ -9,6 +11,14 @@ const symbols = Array.from({ length: 10 }, (_, i) => String.fromCharCode(48 + i)
const specials = ['@', '#', '$', '%', '&', '!', '?', '+', '-']

export const FontFamily = () => {
if (fonts.length === 0) {
return (
<EmptyState title="No Tokens" icon={<TypographyIcon />}>
The panda config does not contain any font family
</EmptyState>
)
}

return (
<Stack gap="10">
{fonts.map((font) => (
Expand Down
10 changes: 10 additions & 0 deletions packages/studio/src/components/font-tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { TokenContent } from '../components/token-content'
import { TokenGroup } from '../components/token-group'
import { Input, Textarea } from './input'
import { StickyTop } from './sticky-top'
import { EmptyState } from './empty-state'
import { TypographyIcon, XMarkIcon } from './icons'

interface FontTokensProps {
text?: string
Expand All @@ -23,6 +25,14 @@ export default function FontTokens(props: FontTokensProps) {
setText(event.target.value)
}

if (fontTokens.length === 0) {
return (
<EmptyState title="No Tokens" icon={<TypographyIcon />}>
The panda config does not contain any `{token}` tokens
</EmptyState>
)
}

return (
<TokenGroup>
<StickyTop>
Expand Down
2 changes: 1 addition & 1 deletion packages/studio/src/components/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function Overview() {
<Logo />

<div className={vstack({ my: '10', textAlign: 'center' })}>
<Yums className={css({ fontSize: '24rem' })} />
<Yums className={css({ fontSize: '24rem', h: '300px' })} />
<span className={css({ fontSize: '7xl', letterSpacing: 'tighter', fontWeight: 'medium' })}>Panda Studio</span>
<p className={css({ fontSize: '2xl' })}>Live documentation for your design tokens (colors, fonts, etc.)</p>
</div>
Expand Down
18 changes: 1 addition & 17 deletions packages/studio/src/components/semantic-color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,13 @@ import { Flex, panda } from '../../styled-system/jsx'
import { ColorWrapper } from './color-wrapper'
import * as context from '../lib/panda-context'

const getSemanticColorValue = (variable: string): string => {
const _name = variable?.match(/var\(\s*--(.*?)\s*\)/)
if (!_name) return variable

const name = _name[1].replaceAll('-', '.')
const token = context.tokens.getByName(name)

if (!token) {
const defaultToken = context.tokens.getByName(`${name}.default`)
return getSemanticColorValue(defaultToken?.value)
}

if (token.value.startsWith('var(--')) return getSemanticColorValue(token.value)
return token.value
}

// remove initial underscore
const cleanCondition = (condition: string) => condition.replace(/^_/, '')

export function SemanticColorDisplay(props: { value: string; condition: string; token?: string }) {
const { value, condition } = props

const tokenValue = getSemanticColorValue(value)
const tokenValue = context.tokens.deepResolveReference(value)

return (
<Flex direction="column" w="full">
Expand Down
13 changes: 12 additions & 1 deletion packages/studio/src/components/sizes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import { Grid, panda } from '../../styled-system/jsx'
import { getSortedSizes } from '../lib/sizes-sort'
import { TokenGroup } from './token-group'
import type { Token } from '@pandacss/token-dictionary'
import { EmptyState } from './empty-state'
import { SizesIcon } from './icons'

export interface SizesProps {
sizes: Token[]
name: string
}

const contentRegex = /^(min|max|fit)-content$/
const unitRegex = /(ch|%)$/

export default function Sizes(props: SizesProps) {
const { sizes } = props
const { sizes, name } = props

const sortedSizes = getSortedSizes(sizes).filter(
(token) =>
Expand All @@ -27,6 +30,14 @@ export default function Sizes(props: SizesProps) {
!unitRegex.test(token.value),
)

if (sortedSizes.length === 0) {
return (
<EmptyState title="No Tokens" icon={<SizesIcon />}>
The panda config does not contain any `{name}`` tokens
</EmptyState>
)
}

return (
<TokenGroup>
<Grid display="grid" columnGap="10" rowGap="2.5" columns={5}>
Expand Down
15 changes: 14 additions & 1 deletion packages/studio/src/components/text-styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EmptyState } from './empty-state'
import { TextStylesIcon } from './icons'
import { TokenContent } from './token-content'
import { TokenGroup } from './token-group'
import type { Dict } from '../../styled-system/types'

export default function TextStyles() {
const textStyles = Object.entries(context.textStyles)
Expand All @@ -18,7 +19,7 @@ export default function TextStyles() {
<panda.div borderColor="card">
<panda.span fontWeight="medium">{name}</panda.span>
</panda.div>
<panda.div flex="auto" my="3" style={styles} truncate>
<panda.div flex="auto" my="3" style={removeEscapeHatchSyntax(styles)} truncate>
Panda textStyles are time saving
</panda.div>
</panda.div>
Expand All @@ -32,3 +33,15 @@ export default function TextStyles() {
</TokenGroup>
)
}

const removeEscapeHatchSyntax = (styles: Dict) => {
return Object.fromEntries(
Object.entries(styles).map(([key, value]) => {
if (typeof value === 'string' && value[0] === '[' && value[value.length - 1] === ']') {
return [key, value.slice(1, -1)]
}

return [key, value]
}),
)
}
42 changes: 24 additions & 18 deletions packages/studio/src/lib/use-color-docs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Token } from '@pandacss/token-dictionary'
import type { Token, TokenExtensions } from '@pandacss/token-dictionary'
import { useState } from 'react'
import * as context from './panda-context'

Expand All @@ -10,33 +10,36 @@ interface Color {
path: string[]
}

type ColorToken = Token & Color
type ColorToken = Token & Color & TokenExtensions

const UNCATEGORIZED_ID = 'uncategorized' as const

const groupByColorPalette = (colors: Token[], filterMethod?: (token: ColorToken) => boolean) => {
const groupByColorPalette = (colors: ColorToken[], filterMethod?: (token: ColorToken) => boolean) => {
const values = colors.filter((color) => !color.isConditional && !color.extensions.isVirtual)

return values.reduce<Record<string, any>>((acc, color) => {
if (!filterMethod?.(color)) return acc
return values.reduce(
(acc, color) => {
if (!filterMethod?.(color)) return acc

const colorPalette = color.extensions.colorPalette || UNCATEGORIZED_ID
const colorPalette = color.extensions.colorPalette || UNCATEGORIZED_ID

if (!(colorPalette in acc)) {
acc[colorPalette] = []
}
if (!(colorPalette in acc)) {
acc[colorPalette] = []
}

const exists = (acc[colorPalette] as any[]).find((tok) => tok.name === color.name)
if (!exists) acc[colorPalette].push(color)
const exists = (acc[colorPalette] as any[]).find((tok) => tok.name === color.name)
if (!exists) acc[colorPalette].push(color)

return acc
}, {})
return acc
},
{} as Record<string, ColorToken[]>,
)
}

const getSemanticTokens = (allTokens: ColorToken[], filterMethod?: (token: ColorToken) => boolean) => {
const getSemanticTokens = (allTokens: Token[], filterMethod?: (token: ColorToken) => boolean) => {
const semanticTokens = allTokens.filter(
(token) => token.type === 'color' && token.isConditional && !token.extensions?.isVirtual,
)
) as ColorToken[]
return semanticTokens
.reduce((acc, nxt) => {
if (!filterMethod) {
Expand All @@ -50,7 +53,7 @@ const getSemanticTokens = (allTokens: ColorToken[], filterMethod?: (token: Color
}
return acc
}, [] as ColorToken[])
.reduce<Record<string, any>>(
.reduce<Record<string, ColorToken>>(
(acc, nxt) => ({
...acc,
[nxt.extensions?.prop]: {
Expand Down Expand Up @@ -85,14 +88,17 @@ export const useColorDocs = () => {
.some((prop) => prop.includes(filterQuery))
}

const colorsInCategories = groupByColorPalette(colors, filterMethod)
const colorsInCategories = groupByColorPalette(colors as ColorToken[], filterMethod)
const uncategorizedColors = colorsInCategories[UNCATEGORIZED_ID]

const categorizedColors = Object.entries<any[]>(colorsInCategories).filter(
([category]) => category !== UNCATEGORIZED_ID,
)

const semanticTokens = Object.entries<Record<string, any>>(getSemanticTokens(allTokens, filterMethod))
const semanticTokens = Object.entries<Record<string, any>>(getSemanticTokens(allTokens, filterMethod)) as [
string,
Record<string, ColorToken>,
][]
const hasResults =
!!categorizedColors.length || !!uncategorizedColors?.length || !!Object.values(semanticTokens).length

Expand Down
5 changes: 2 additions & 3 deletions packages/studio/src/pages/sizes.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
import Sizes from '../components/sizes'
import Sizes from '../components/sizes'
import Layout from '../layouts/Layout.astro'
import Sidebar from '../layouts/Sidebar.astro'
import * as context from '../lib/panda-context'
Expand All @@ -9,7 +9,6 @@ const tokens = context.getTokens('sizes')

<Layout>
<Sidebar title="Sizes">
<Sizes sizes={tokens} client:load />
<Sizes sizes={tokens} name="sizes" client:load />
</Sidebar>
</Layout>

8 changes: 4 additions & 4 deletions packages/studio/src/pages/spacing.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
import Sizes from '../components/sizes'
import Sizes from '../components/sizes'
import Layout from '../layouts/Layout.astro'
import Sidebar from '../layouts/Sidebar.astro'
import * as context from '../lib/panda-context'
Expand All @@ -8,7 +8,7 @@ const tokens = context.getTokens('spacing')
---

<Layout>
<Sidebar title="Spacing">
<Sizes sizes={tokens} client:load />
</Sidebar>
<Sidebar title="Spacing">
<Sizes sizes={tokens} name="spacing" client:load />
</Sidebar>
</Layout>
7 changes: 5 additions & 2 deletions packages/studio/styled-system/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,6 @@
}

@layer utilities{

.d_flex {
display: flex;
}
Expand Down Expand Up @@ -957,6 +956,10 @@
margin-block: var(--spacing-10);
}

.h_300px {
height: 300px;
}

.tracking_tighter {
letter-spacing: var(--letter-spacings-tighter);
}
Expand Down Expand Up @@ -1422,4 +1425,4 @@
padding-inline: var(--spacing-8);
}
}
}
}
Loading

0 comments on commit 93dc9f5

Please sign in to comment.