Skip to content

Commit a4c9246

Browse files
committed
Complete rewrite of project in Remix
1 parent 95d0b6a commit a4c9246

File tree

115 files changed

+17758
-8150
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+17758
-8150
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.eslintrc.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
module.exports = {
2+
root: true,
3+
parser: '@typescript-eslint/parser',
4+
env: {
5+
node: true,
6+
browser: true,
7+
},
8+
ignorePatterns: ['node_modules/', 'dist/', 'build/'],
9+
settings: {
10+
react: {version: '16.9.0'},
11+
},
12+
extends: [
13+
'sanity',
14+
'sanity/react',
15+
'sanity/import',
16+
'plugin:react-hooks/recommended',
17+
'prettier',
18+
],
19+
rules: {
20+
'no-use-before-define': 'off',
21+
'@typescript-eslint/no-var-requires': 'off',
22+
'import/no-extraneous-dependencies': 'off', // because of parts
23+
'import/no-unresolved': ['error', {ignore: ['.*:.*']}], // because of parts
24+
'prettier/prettier': [
25+
'error',
26+
{
27+
semi: false,
28+
printWidth: 100,
29+
bracketSpacing: false,
30+
singleQuote: true,
31+
},
32+
],
33+
'sort-imports': 'off', // prefer import/order
34+
'react/jsx-no-bind': [
35+
1,
36+
{
37+
ignoreDOMComponents: true,
38+
},
39+
],
40+
'react/forbid-prop-types': [0],
41+
},
42+
plugins: ['prettier', 'react'],
43+
overrides: [
44+
{
45+
files: ['*.ts', '*.tsx'],
46+
rules: {
47+
'no-undef': 'off',
48+
'react/react-in-jsx-scope': 'off',
49+
50+
// No more prop-types
51+
'react/prop-types': 'off',
52+
'react/require-default-props': 'off',
53+
54+
// Struggles with ~ paths
55+
'import/no-unresolved': 'off',
56+
'import/extensions': 'off',
57+
58+
// Rule from Studio
59+
'no-process-env': 'off',
60+
},
61+
},
62+
],
63+
}

.gitignore

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
node_modules
21
.DS_Store
3-
dist
4-
*.local
2+
node_modules
3+
4+
/.cache
5+
/.mf
6+
/build
7+
/public/build
8+
/app/styles/output.css
9+
10+
worker.js

.npmignore

Lines changed: 0 additions & 4 deletions
This file was deleted.

.prettierrc.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
semi: false,
3+
printWidth: 100,
4+
bracketSpacing: false,
5+
singleQuote: true,
6+
}

Dockerfile

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# base node image
2+
FROM node:17-bullseye-slim as base
3+
4+
# Install Chromium for puppeteer
5+
# RUN apt-get update && apt-get install -y chromium
6+
7+
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
8+
# ENV CHROME_BIN=/usr/bin/chromium
9+
10+
# Install openssl for Prisma
11+
# RUN apt-get update && apt-get install -y openssl
12+
13+
# Install all node_modules, including dev dependencies
14+
FROM base as deps
15+
16+
RUN mkdir /app -p
17+
WORKDIR /app
18+
19+
ADD package.json package-lock.json ./
20+
RUN npm install --production=false
21+
22+
# Setup production node_modules
23+
FROM base as production-deps
24+
25+
RUN mkdir /app -p
26+
WORKDIR /app
27+
28+
COPY --from=deps /app/node_modules /app/node_modules
29+
ADD package.json package-lock.json ./
30+
# RUN npm prune --production
31+
32+
# Build the app
33+
FROM base as build
34+
35+
ENV NODE_ENV=production
36+
37+
RUN mkdir /app
38+
WORKDIR /app
39+
40+
COPY --from=deps /app/node_modules /app/node_modules
41+
42+
# If we're using Prisma, uncomment to cache the prisma schema
43+
# ADD prisma .
44+
# RUN npx prisma generate
45+
46+
ADD . .
47+
RUN npm run build
48+
49+
# Finally, build the production image with minimal footprint
50+
FROM base
51+
52+
ENV NODE_ENV=production
53+
54+
RUN mkdir /app
55+
WORKDIR /app
56+
57+
COPY --from=production-deps /app/node_modules /app/node_modules
58+
59+
# Uncomment if using Prisma
60+
# COPY --from=build /app/node_modules/.prisma /app/node_modules/.prisma
61+
62+
COPY --from=build /app/build /app/build
63+
COPY --from=build /app/public /app/public
64+
ADD . .
65+
66+
CMD ["npm", "run", "start"]

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Palette Generator and API for Tailwind CSS
2+
3+
Visit [tailwind.simeongriggs.dev](https://tailwind.simeongriggs.dev) to use the GUI and API.
4+
5+
Read the [2.0.0 launch blog post](https://www.simeongriggs.dev/using-the-tailwind-css-palette-generator-and-api) for details on how it works.
6+
7+
## Credits
8+
9+
Created by [Simeon Griggs](https://simeongriggs.dev/)
10+
11+
Contributions by [kevnk](https://truefrontierapps.com/)

app/components/ButtonIcon.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, {MouseEventHandler, ReactNode} from 'react'
2+
import {usePopperTooltip} from 'react-popper-tooltip'
3+
4+
const classNames = `border p-1 transition-colors duration-100 rounded`
5+
const disabledClassNames = `bg-gray-100 border-gray-200 text-gray-200 cursor-not-allowed pointer-events-none`
6+
const toneClassNames = {
7+
danger: `border-gray-200 text-gray-400 hover:text-red-500 hover:border-red-500 hover:bg-red-100`,
8+
success: `border-gray-200 text-gray-400 hover:text-green-500 hover:border-green-500 hover:bg-green-100`,
9+
}
10+
const selectedClassNames = {
11+
danger: `text-red-500 border-red-500 bg-red-100`,
12+
success: `text-green-500 border-green-500 bg-green-100`,
13+
}
14+
const toneTooltipClassNames = {
15+
danger: `text-red-700`,
16+
success: `text-green-700`,
17+
}
18+
19+
export default function ButtonIcon({
20+
title,
21+
onClick,
22+
icon,
23+
href = ``,
24+
disabled = false,
25+
selected = false,
26+
tone = 'success',
27+
tabIndex = -1,
28+
}: {
29+
title: string
30+
icon: ReactNode
31+
href?: string
32+
onClick?: MouseEventHandler | undefined
33+
disabled?: boolean
34+
selected?: boolean
35+
tone?: 'danger' | 'success'
36+
tabIndex?: number
37+
}) {
38+
const className = [
39+
disabled ? disabledClassNames : toneClassNames[tone],
40+
selected ? selectedClassNames[tone] : ``,
41+
classNames,
42+
]
43+
.filter(Boolean)
44+
.join(' ')
45+
46+
const {getArrowProps, getTooltipProps, setTooltipRef, setTriggerRef, visible} = usePopperTooltip()
47+
48+
if (href) {
49+
return (
50+
<a
51+
href={href}
52+
className={className}
53+
tabIndex={tabIndex}
54+
title={title}
55+
target="_blank"
56+
rel="noopener noreferrer"
57+
>
58+
<>
59+
{icon ? React.createElement(icon, {className: `w-5 h-auto`}) : null}
60+
<span className="sr-only">{title}</span>
61+
</>
62+
</a>
63+
)
64+
}
65+
66+
return (
67+
<>
68+
<button
69+
ref={setTriggerRef}
70+
type="button"
71+
className={className}
72+
onClick={onClick}
73+
disabled={disabled}
74+
tabIndex={tabIndex}
75+
// title={title}
76+
>
77+
{icon ? React.createElement(icon, {className: `w-5 h-auto`}) : null}
78+
<span className="sr-only">{title}</span>
79+
</button>
80+
{visible && (
81+
<div
82+
ref={setTooltipRef}
83+
{...getTooltipProps({
84+
className: `w-32 text-center z-50 bg-white p-2 rounded shadow text-xs font-medium ${toneTooltipClassNames[tone]}`,
85+
})}
86+
>
87+
<div
88+
{...getArrowProps({
89+
className: `absolute bottom-full bg-white h-2 w-4 pointer-events-none`,
90+
style: {clipPath: `polygon(50% 0%, 0% 100%, 100% 100%)`},
91+
})}
92+
/>
93+
{title}
94+
</div>
95+
)}
96+
</>
97+
)
98+
}

app/components/ColorPicker.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, {useEffect, useState} from 'react'
2+
import {HexColorPicker} from 'react-colorful'
3+
import {useDebounce} from 'usehooks-ts'
4+
import {Popover} from '@headlessui/react'
5+
import {ColorSwatchIcon} from '@heroicons/react/solid'
6+
7+
export default function ColorPicker({
8+
color,
9+
onChange,
10+
ringStyle,
11+
}: {
12+
color: string
13+
onChange: Function
14+
ringStyle: React.CSSProperties
15+
}) {
16+
const [value, setValue] = useState<string>(color)
17+
const debouncedValue = useDebounce<string>(value, 500)
18+
19+
useEffect(() => (value ? onChange(value.toUpperCase()) : null), [debouncedValue])
20+
21+
return (
22+
<Popover className="relative">
23+
<Popover.Button
24+
style={ringStyle}
25+
className="w-full p-2 border border-gray-200 bg-gray-50 focus:outline-none focus:ring focus:bg-gray-100 focus:border-gray-300 text-gray-500 focus:text-gray-900"
26+
>
27+
<ColorSwatchIcon className="w-6 h-auto" />
28+
<span className="sr-only">Open Color Picker</span>
29+
</Popover.Button>
30+
31+
<Popover.Panel className="absolute right-0 z-10 bg-white shadow p-1 translate-y-1">
32+
<HexColorPicker color={value.startsWith(`#`) ? value : `#${value}`} onChange={setValue} />
33+
</Popover.Panel>
34+
</Popover>
35+
)
36+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react'
2+
3+
import {PaletteConfig} from '~/types/palette'
4+
5+
const graphHeight = 40
6+
7+
export default function DistributionGraph({palettes}: {palettes: PaletteConfig[]}) {
8+
return (
9+
<section className="grid grid-cols-1 gap-2">
10+
<div className="text-lg font-medium text-center">
11+
<h2>Lightness/Luminance Distribution 0-100</h2>
12+
</div>
13+
14+
<div
15+
style={{height: graphHeight * palettes.length}}
16+
className="relative rounded bg-gray-800 flex justify-between h-full overflow-hidden"
17+
>
18+
{palettes.map((palette, index) => (
19+
<React.Fragment key={palette.value}>
20+
{palette.swatches.map((swatch) => (
21+
<div
22+
key={swatch.stop}
23+
style={{
24+
backgroundColor: swatch.hex,
25+
transitionDelay: `${swatch.stop / 2}ms`,
26+
top: (index + 1) * graphHeight - graphHeight / 2,
27+
left: `${100 - swatch.l}%`,
28+
}}
29+
className="transition duration-500 absolute z-10 border-2 border-white shadow rounded-full transform -translate-y-1/2 -translate-x-1/2 w-5 h-5"
30+
/>
31+
))}
32+
</React.Fragment>
33+
))}
34+
<div className="absolute p-2 leading-none bottom-0 left-0 text-gray-100 text-xs font-bold">
35+
100 (White)
36+
</div>
37+
<div className="absolute p-2 leading-none bottom-0 right-0 text-gray-100 text-xs font-bold">
38+
0 (Black)
39+
</div>
40+
<div
41+
className="absolute inset-0 border-t border-gray-700"
42+
style={{top: '50%', height: '50%'}}
43+
/>
44+
<div className="border-transparent h-full border-l" />
45+
<div className="border-gray-700 h-full border-l border-dashed" />
46+
<div className="border-gray-700 h-full border-l border-dashed" />
47+
<div className="border-gray-700 h-full border-l border-dashed" />
48+
<div className="border-gray-700 h-full border-l border-dashed" />
49+
<div className="border-gray-700 h-full border-l" />
50+
<div className="border-gray-700 h-full border-l border-dashed" />
51+
<div className="border-gray-700 h-full border-l border-dashed" />
52+
<div className="border-gray-700 h-full border-l border-dashed" />
53+
<div className="border-gray-700 h-full border-l border-dashed" />
54+
<div className="border-transparent h-full border-l" />
55+
</div>
56+
</section>
57+
)
58+
}

0 commit comments

Comments
 (0)