diff --git a/components/ColorPickerInput.tsx b/components/ColorPickerInput.tsx index be631d6..d10da9e 100644 --- a/components/ColorPickerInput.tsx +++ b/components/ColorPickerInput.tsx @@ -1,6 +1,6 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ -import { Input, useTheme } from '@geist-ui/react'; +import { Input } from '@geist-ui/react'; import React from 'react'; import ColorPicker from './ColorPicker'; import InputLabel from './InputLabel'; @@ -23,7 +23,6 @@ const ColorPickerInput: React.FC = ({ const handleColor = (v: string) => { setColor(v, type); }; - const theme = useTheme(); const [open, setOpen] = React.useState(false); return ( <> @@ -58,14 +57,14 @@ const ColorPickerInput: React.FC = ({ .picker-box { width: 50px; background-color: ${color}; - border: 1px solid ${theme.palette.accents_2}; + border: 1px solid #333; border-right: 0; border-top-left-radius: 5px; border-bottom-left-radius: 5px; } .picker-container { - background-color: ${theme.palette.accents_1}; - border: 1px solid ${theme.palette.accents_2}; + background-color: #111; + border: 1px solid #333; padding: 20px; z-index: 1000; top: 125%; diff --git a/labs/components/LabColorPicker.tsx b/labs/components/LabColorPicker.tsx new file mode 100644 index 0000000..280014f --- /dev/null +++ b/labs/components/LabColorPicker.tsx @@ -0,0 +1,91 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import { Input, useClickAway } from '@geist-ui/react'; +import { ChevronDown, ChevronUp, XCircle } from '@geist-ui/react-icons'; +import React from 'react'; +import { HexColorPicker } from 'react-colorful'; + +interface Props { + color: string; + index: number; + changePaletteColor: (v: string, i: number) => void; + removePalette: (i: number) => void; + handleLegendPosChange: (i: number, type: boolean) => void; +} + +const LabColorPicker: React.FC = ({ + color, + index, + changePaletteColor, + removePalette, + handleLegendPosChange +}) => { + const [open, setOpen] = React.useState(false); + const [p, setP] = React.useState(color); + const ref = React.useRef>(); + // @ts-ignore + useClickAway(ref, () => changePaletteColor(p, index)); + return ( +
+
+ setP(e)} /> +
+
+
setOpen(!open)} + /> + +
removePalette(index)}> + +
+
+ handleLegendPosChange(index, true)} /> + handleLegendPosChange(index, false)} /> +
+
+ +
+ ); +}; + +export default LabColorPicker; diff --git a/labs/usa.map.tsx b/labs/usa.map.tsx new file mode 100644 index 0000000..c639f46 --- /dev/null +++ b/labs/usa.map.tsx @@ -0,0 +1,329 @@ +import React from 'react'; + +const UsaMap = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default UsaMap; diff --git a/labs/utils/createRandomData.ts b/labs/utils/createRandomData.ts new file mode 100644 index 0000000..93063ab --- /dev/null +++ b/labs/utils/createRandomData.ts @@ -0,0 +1,10 @@ +const createRandomData = (obj : {[key: string]: string}): {[key: string]: number} => { + const data = {}; + Object.keys(obj).forEach((e) => { + // @ts-ignore + data[e] = Math.floor(Math.random() * 10000); + }); + return data; +} + +export default createRandomData; \ No newline at end of file diff --git a/labs/utils/getLegendList.ts b/labs/utils/getLegendList.ts new file mode 100644 index 0000000..3e908e9 --- /dev/null +++ b/labs/utils/getLegendList.ts @@ -0,0 +1,11 @@ +const getLegendList = (sortObj: { [key: string]: number } , count: number): number[] => { + const endVal = Object.values(sortObj)[Object.values(sortObj).length - 1]; + const lastLegend = Math.pow(10, endVal.toString().length); + const legendList: number[] = []; + for (let i = 1; i <= count; i++) { + legendList.push(i * (lastLegend / count)); + } + return legendList +}; + +export default getLegendList; diff --git a/labs/utils/getMapData.ts b/labs/utils/getMapData.ts new file mode 100644 index 0000000..66a474f --- /dev/null +++ b/labs/utils/getMapData.ts @@ -0,0 +1,34 @@ +interface Props { + sortedData: { [key: string]: number }; + legendList: number[]; + paletteData: string[] +} + +const getMapData= ({ sortedData , legendList ,paletteData }: Props) => { + const mapData: {fill: string , code: string , hide: boolean, val: number}[] = []; + const closest = (t: number) => + legendList.reduce((prev, curr) => Math.abs(curr - t) < Math.abs(prev - t) ? curr : prev); + Object.keys(sortedData).forEach((e) => { + const val = sortedData[e]; + const closestVal = closest(val); + const idx = legendList.indexOf(closestVal); + if (val > closestVal) { + mapData.push({ + fill: paletteData[idx + 1], + code: e, + hide: false, + val, + }); + } else { + mapData.push({ + fill: paletteData[idx], + code: e, + hide: false, + val, + }); + } + }); + return mapData; +} + +export default getMapData diff --git a/labs/utils/getPaletteData.ts b/labs/utils/getPaletteData.ts new file mode 100644 index 0000000..1f6bdd3 --- /dev/null +++ b/labs/utils/getPaletteData.ts @@ -0,0 +1,10 @@ +import Gradient from 'javascript-color-gradient'; + +const colorGradient = new Gradient(); + +const getPaletteData = (count: number , colorList: string[]) => { + const paletteData = colorGradient.setGradient(...colorList).setMidpoint(count).getArray(); + return paletteData; +}; + +export default getPaletteData; diff --git a/labs/utils/sortObject.ts b/labs/utils/sortObject.ts new file mode 100644 index 0000000..97f1fb0 --- /dev/null +++ b/labs/utils/sortObject.ts @@ -0,0 +1,4 @@ +const sortObject = (d : {[key: string]: number}): {[key: string]: number}=> Object.entries(d) + .sort(([, a], [, b]) => a - b) + .reduce((r, [k, v]) => ({ ...r, [k]: v }), {}) +export default sortObject; \ No newline at end of file diff --git a/package.json b/package.json index 9a454f4..52c8cca 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@mdx-js/loader": "^1.6.22", "@next/mdx": "^10.2.3", "inter-ui": "^3.18.1", + "javascript-color-gradient": "^1.3.2", "jotai": "^0.16.0", "jspdf": "^2.3.1", "next-seo": "^4.23.0", diff --git a/pages/labs/index.tsx b/pages/labs/index.tsx new file mode 100644 index 0000000..7083e78 --- /dev/null +++ b/pages/labs/index.tsx @@ -0,0 +1,177 @@ +import { UsaStateCodes } from '@/data/Usa/UsaStateCodes'; +import LabColorPicker from '@/labs/components/LabColorPicker'; +import createRandomData from '@/labs/utils/createRandomData'; +import getLegendList from '@/labs/utils/getLegendList'; +import getMapData from '@/labs/utils/getMapData'; +import getPaletteData from '@/labs/utils/getPaletteData'; +import sortObject from '@/labs/utils/sortObject'; +import MainLayout from '@/layouts/MainLayout'; +import { LabMapStoreType } from '@/typings/lab.store'; +import { Button, Input, Spacer } from '@geist-ui/react'; +import UsaMap from 'labs/usa.map'; +import React from 'react'; + +const reOrderArrayElements = (arr: string[], val: string, oldIdx: number, newIdx: number) => { + // remove it + arr.splice(oldIdx, 1); + // add it + arr.splice(newIdx, 0, val); + return arr; +}; + +const Lab = () => { + const [data, setData] = React.useState({ + mapData: [], + count: 10, + paletteArr: ['#f9d56e', '#ff1e56'] + }); + const changePaletteColor = (v: string, i: number) => { + const copy = data.paletteArr; + copy[i] = v; + setData((st) => ({ + ...st, + paletteArr: copy + })); + }; + const addPalette = () => { + const el = document.getElementById('add-palette'); + if (el) { + const copy = data.paletteArr; + // @ts-ignore + copy.push(el.value); + setData((st) => ({ + ...st, + paletteArr: copy + })); + } + }; + const removePalette = (i: number) => { + const copy = data.paletteArr; + copy.splice(i, 1); + setData((st) => ({ + ...st, + paletteArr: copy + })); + }; + const handleLegendPosChange = (idx: number, up: boolean) => { + const len = data.paletteArr.length; + const copy = data.paletteArr; + if (up) { + if (idx === 0) { + reOrderArrayElements(copy, copy[idx], idx, len - 1); + } else { + reOrderArrayElements(copy, copy[idx], idx, idx - 1); + } + } else if (!up) { + if (idx === len - 1) { + reOrderArrayElements(copy, copy[idx], idx, 0); + } else { + reOrderArrayElements(copy, copy[idx], idx, idx + 1); + } + } + // @ts-ignore + setData((prev) => ({ + ...prev, + paletteArr: copy + })); + }; + const getRandomData = () => { + const randData = createRandomData(UsaStateCodes); + const sortedData = sortObject(randData); + const legendList = getLegendList(sortedData, data.count); + const paletteData = getPaletteData(data.count, data.paletteArr); + const mapData = getMapData({ + sortedData, + legendList, + paletteData + }); + mapData.forEach((e) => { + const el = document.getElementById(e.code); + if (el) { + el.style.fill = e.fill; + } + }); + setData({ + mapData, + count: 10, + paletteArr: data.paletteArr + }); + }; + data.mapData.forEach((e) => { + const el = document.getElementById(e.code); + if (el) { + el.style.fill = e.fill; + } + }); + return ( + +
+
+ + {data.paletteArr.map((e, i) => ( + + ))} +
+ + + +
+ + setData((p) => ({ + ...p, + count: +e.target.value + })) + } + /> +
+ + + + + + + + + + {data.mapData.map((e) => ( + + + {/* @ts-ignore */} + + + + ))} + +
CodeNameValue
{e.code}{UsaStateCodes[e.code]}{e.val}
+
+
+ +
+ +
+ ); +}; + +export default Lab; diff --git a/store/lab.store.ts b/store/lab.store.ts new file mode 100644 index 0000000..77c617e --- /dev/null +++ b/store/lab.store.ts @@ -0,0 +1,9 @@ + +import { LabMapStoreType } from '@/typings/lab.store' +import { atom } from 'jotai' + +export const labAtom = atom({ + mapData: [], + count: 10, + paletteArr: ['red','blue'] +}) diff --git a/styles/main.css b/styles/main.css index 39fcc22..95aa32f 100644 --- a/styles/main.css +++ b/styles/main.css @@ -176,3 +176,7 @@ table td:nth-child(1) { table tr:not(:last-of-type) td { border-bottom: 1px solid #333; } +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} diff --git a/tsconfig.json b/tsconfig.json index 316a264..349c81f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "@/data/*": ["data/*"], "@/store/*": ["store/*"], "@/configs/*": ["configs/*"], + "@/labs/*": ["labs/*"], }, "strictNullChecks": true, "moduleResolution": "node", diff --git a/typings/javascript-color-gradient.d.ts b/typings/javascript-color-gradient.d.ts new file mode 100644 index 0000000..0a4d496 --- /dev/null +++ b/typings/javascript-color-gradient.d.ts @@ -0,0 +1 @@ +declare module 'javascript-color-gradient' \ No newline at end of file diff --git a/typings/lab.store.d.ts b/typings/lab.store.d.ts new file mode 100644 index 0000000..b88c0ac --- /dev/null +++ b/typings/lab.store.d.ts @@ -0,0 +1,12 @@ +interface LabMapType { + fill: string; + code: string; + hide: boolean; + val: number; +} + +export interface LabMapStoreType { + mapData: LabMapType[]; + paletteArr: string[] + count: number; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4c20d8d..b865e90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2792,6 +2792,11 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" +javascript-color-gradient@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/javascript-color-gradient/-/javascript-color-gradient-1.3.2.tgz#6b19f3021721c1463ca2ed02cd1c1ed6b716e8e2" + integrity sha512-P/VjoTPdfRmMOkfR65m1BQTlX/hYphZIj5u1Ntvpxf+9011uXjazbEOQ63g3EhlgzHYa2uwVU3RVgy3gJuLdlQ== + jest-worker@24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5"