Skip to content
This repository was archived by the owner on Mar 25, 2025. It is now read-only.

Commit 41ac4af

Browse files
tripleWdotcomTigge
authored andcommitted
feat(docs): ThemeCreator
Generates a theme file with custom colors
1 parent 544dd76 commit 41ac4af

File tree

4 files changed

+518
-0
lines changed

4 files changed

+518
-0
lines changed

packages/docs/src/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Home } from './Home'
1818
import { Content } from './Content'
1919

2020
import { Components } from './types'
21+
import { ThemeCreator } from './mdx/themeCreator/ThemeCreator'
2122

2223
const practicalCoreModuleContext = require.context('./', true, /\.mdx$/)
2324

@@ -109,6 +110,9 @@ ReactDOM.render(
109110
<Route exact path="/">
110111
<Home />
111112
</Route>
113+
<Route exact path="/theme-creator">
114+
<ThemeCreator />
115+
</Route>
112116
{componentDb.map(({ route, component: Component }) => (
113117
<Route key={route} path={route}>
114118
<Component />
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const meta = {
2+
name: 'Theme Creator',
3+
route: '/theme-creator',
4+
menu: 'Styles',
5+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import React, { useCallback, useMemo, useRef, useState } from 'react'
2+
import styled from 'styled-components'
3+
import {
4+
palette,
5+
Tooltip,
6+
shape,
7+
ColorName,
8+
CSSColor,
9+
spacing,
10+
componentSize,
11+
PopOver,
12+
} from 'practical-react-components-core'
13+
import { useClickOutside } from 'react-hooks-shareable'
14+
15+
const PALETTE_CONTAINER_HEIGHT = 300
16+
17+
interface ColorBoxProps {
18+
readonly color?: string
19+
readonly selected: boolean
20+
}
21+
const ColorBox = styled.div<ColorBoxProps>`
22+
background-color: ${({ color }) => color};
23+
cursor: pointer;
24+
height: ${componentSize.small};
25+
width: ${componentSize.small};
26+
${({ selected }) =>
27+
selected
28+
? `height: ${componentSize.mini};
29+
width: ${componentSize.mini};
30+
margin:2px`
31+
: '0px'};
32+
`
33+
const SelectedColorBox = styled.div<ColorBoxProps>`
34+
${({ selected }) => (selected ? `border:2px solid;` : 'none')};
35+
`
36+
37+
const ColorSegment = styled.div`
38+
background-color: ${({ color }) => color};
39+
cursor: pointer;
40+
height: 20px;
41+
border-radius: ${shape.radius.medium};
42+
margin: 0 ${spacing.medium};
43+
`
44+
interface PaletteContainerProps {
45+
readonly changeWidth?: number
46+
}
47+
48+
const PaletteContainer = styled.div<PaletteContainerProps>`
49+
overflow-y: scroll;
50+
background-color: ${({ theme }) => theme.color.background()};
51+
border: ${({ theme }) => theme.color.element13()} 2px solid;
52+
border-radius: ${shape.radius.medium};
53+
height: ${PALETTE_CONTAINER_HEIGHT}px;
54+
display: flex;
55+
flex-wrap: wrap;
56+
width: ${({ changeWidth }) => changeWidth}px;
57+
min-width: 115px;
58+
`
59+
interface PaletteColorProps {
60+
readonly colorName: ColorName
61+
readonly colorValue: CSSColor
62+
readonly onChosenColor: (colorName: ColorName) => void
63+
readonly selected: boolean
64+
}
65+
66+
const PaletteColor: React.VFC<PaletteColorProps> = ({
67+
colorName,
68+
colorValue,
69+
onChosenColor,
70+
selected,
71+
}) => {
72+
const onClick = useCallback(() => {
73+
onChosenColor(colorName)
74+
}, [colorName, onChosenColor])
75+
return (
76+
<div key={colorName}>
77+
<Tooltip text={colorName}>
78+
<SelectedColorBox selected={selected}>
79+
<ColorBox
80+
selected={selected}
81+
color={colorValue()}
82+
onClick={onClick}
83+
/>
84+
</SelectedColorBox>
85+
</Tooltip>
86+
</div>
87+
)
88+
}
89+
90+
export interface ColorPickerProps {
91+
readonly onChange: (value: ColorName) => void
92+
readonly value: ColorName
93+
}
94+
95+
export const ColorPicker: React.VFC<ColorPickerProps> = ({
96+
onChange,
97+
value,
98+
}) => {
99+
const [directionUp, setDirectionUp] = useState(false)
100+
const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null)
101+
const [open, setOpen] = useState(false)
102+
const checkWidth = useRef<HTMLDivElement | null>(null)
103+
const currentWidth = checkWidth.current?.clientWidth
104+
const colorSegment = useMemo(() => palette[value](), [value])
105+
106+
const checkDirection = useCallback(() => {
107+
const { clientHeight } = document.documentElement
108+
const elem = anchorEl?.getBoundingClientRect()
109+
if (elem) {
110+
const diff = Math.floor(clientHeight - elem.bottom)
111+
112+
// 300px is the height of <PaletteContainer>
113+
if (diff < PALETTE_CONTAINER_HEIGHT) setDirectionUp(true)
114+
}
115+
}, [anchorEl])
116+
117+
const toggle = useCallback(() => {
118+
setOpen(o => !o)
119+
checkDirection()
120+
}, [checkDirection])
121+
122+
const handler = useClickOutside(() => {
123+
setOpen(false)
124+
})
125+
126+
const removePopOver = useCallback(() => {
127+
setOpen(o => !o)
128+
}, [])
129+
130+
return (
131+
<>
132+
<div ref={setAnchorEl}>
133+
<ColorSegment
134+
color={colorSegment}
135+
onClick={toggle}
136+
onPointerDown={handler}
137+
ref={checkWidth}
138+
>
139+
{open ? (
140+
<PopOver
141+
horizontalAlignment="center"
142+
horizontalPosition="center"
143+
verticalAlignment={directionUp ? 'bottom' : 'top'}
144+
verticalPosition={directionUp ? 'top' : 'bottom'}
145+
anchorEl={anchorEl}
146+
onScroll={removePopOver}
147+
>
148+
<PaletteContainer changeWidth={currentWidth}>
149+
{(
150+
Object.entries(palette) as ReadonlyArray<
151+
[ColorName, CSSColor]
152+
>
153+
)
154+
// Filter out transparent "color"
155+
.filter(([colorName]) => colorName !== 'transparent')
156+
.map(([colorName, colorValue]) => (
157+
<PaletteColor
158+
key={colorName}
159+
selected={value === colorName}
160+
colorValue={colorValue}
161+
onChosenColor={onChange}
162+
colorName={colorName}
163+
/>
164+
))}
165+
</PaletteContainer>
166+
</PopOver>
167+
) : null}
168+
</ColorSegment>
169+
</div>
170+
</>
171+
)
172+
}

0 commit comments

Comments
 (0)