Skip to content

Commit

Permalink
feat(theme-generator): add theme-generator component
Browse files Browse the repository at this point in the history
  • Loading branch information
moecasts committed Jul 4, 2024
1 parent a2b7619 commit 308fa84
Show file tree
Hide file tree
Showing 23 changed files with 904 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/casts-theme-generator/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'father';
import { StandardConfig } from '@casts/standard';

export default defineConfig({
...StandardConfig.father,
});
3 changes: 3 additions & 0 deletions packages/casts-theme-generator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
/dist
.DS_Store
23 changes: 23 additions & 0 deletions packages/casts-theme-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# @casts/theme-generator

[![NPM version](https://img.shields.io/npm/v/@casts/theme-generator.svg?style=flat)](https://npmjs.org/package/@casts/theme-generator)
[![NPM downloads](http://img.shields.io/npm/dm/@casts/theme-generator.svg?style=flat)](https://npmjs.org/package/@casts/theme-generator)

## Install

```bash
$ pnpm add @casts/theme-generator
```

```bash
$ pnpm run dev
$ pnpm run build
```

## Options

TODO

## License

MIT © [moecasts <moecasts.caster@gmail.com>](https://github.com/moecasts <moecasts.caster@gmail.com>)
16 changes: 16 additions & 0 deletions packages/casts-theme-generator/docs/_theme-generator.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: ThemeGenerator
group:
title: General
nav:
title: Components
path: /components
---

## Installation

```bash
$ pnpm add @casts/theme-generator
```

TODO
16 changes: 16 additions & 0 deletions packages/casts-theme-generator/docs/_theme-generator.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: ThemeGenerator
group:
title: 通用
nav:
title: 组件
path: /components
---

## 安装

```bash
$ pnpm add @casts/theme-generator
```

TODO
Empty file.
58 changes: 58 additions & 0 deletions packages/casts-theme-generator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@casts/theme-generator",
"version": "0.0.1",
"description": "a react theme generator for casts design component",
"authors": [
"moecasts <moecasts.caster@gmail.com>"
],
"license": "MIT",
"repository": "https://github.com/moecasts/casts-design.git",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"scripts": {
"dev": "father dev",
"build": "father build",
"build:deps": "father prebundle",
"prepublishOnly": "father doctor && pnpm run build",
"test": "vitest",
"test:coverage": "vitest run --coverage"
},
"keywords": [],
"files": [
"dist",
"compiled"
],
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@casts/standard": "workspace:^0.0.1",
"@swc/core": "^1.3.82",
"@testing-library/jest-dom": "^6.1.2",
"@testing-library/react": "^14.0.0",
"@vitest/coverage-v8": "^1.3.1",
"father": "^4.3.2",
"jsdom": "^22.1.0",
"ts-node": "^10.9.1",
"typescript": "5.5.1-rc",
"vite": "^4.4.9",
"vitest": "^1.3.1"
},
"peerDependencies": {
"react": ">=17"
},
"dependencies": {
"@casts/common": "workspace:^0.0.1",
"@casts/config-provider": "workspace:^0.0.1",
"@casts/drawer": "workspace:^0.0.1",
"@casts/switch": "workspace:^0.0.1",
"@casts/theme": "workspace:^0.0.1",
"@ctrl/tinycolor": "^4.0.3",
"clsx": "^2.0.0",
"react-color-palette": "^7.1.0"
},
"sideEffects": [
"**/*.{css,scss,sass}"
]
}
37 changes: 37 additions & 0 deletions packages/casts-theme-generator/src/components/color-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { noop, useControlled } from '@casts/common';
import {
ColorPicker as BaseColorPicker,
ColorService,
IColor,
} from 'react-color-palette';

export type UseColorPickerProps = Omit<
Parameters<typeof BaseColorPicker>[0],
'color' | 'onChange'
> & {
color?: string;
defaultColor?: string;
onChange?: (color: string) => void;
};
export type ColorPickerProps = UseColorPickerProps;

export const useColorPicker = (props: UseColorPickerProps) => {
const { onChange = noop, ...rest } = props;
const [color, setColor] = useControlled<string>(props, 'color', onChange);

const handleChange = (iColor: IColor) => {
setColor(iColor.hex);
};

return {
...rest,
color: ColorService.convert('hex', color),
onChange: handleChange,
};
};

export const ColorPicker = (props: ColorPickerProps) => {
const { color, onChange, ...rest } = useColorPicker(props);

return <BaseColorPicker {...rest} color={color} onChange={onChange} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-theme-generator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { CSSProperties, useEffect, useRef } from 'react';
import {
noop,
useCircleTransition,
useControlled,
useDebounceFn,
} from '@casts/common';
import { useConfig } from '@casts/config-provider';
import { clsx } from 'clsx';

import { MainColor, ThemeMode, UseThemeGeneratorProps } from '../types';
import {
appendThemeVariablesToHead,
generatePalettes,
toCssVariables,
} from '../utils/generate-palette';

export const useThemeGenerator = (props: UseThemeGeneratorProps) => {
const { className, style, addThemeCodeOnMounted } = props;

const [visible, onVisibleChange] = useControlled<boolean>(
props,
'visible',
props.onVisibleChange || noop,
);

const { getPrefixCls } = useConfig();

/* --------------------------------- classes and styles ---------------------------------------- */
const prefixCls = getPrefixCls('theme-generator');

const classes = clsx(prefixCls, className);
const styles: CSSProperties = { ...style };

const drawerClasses = `${prefixCls}-drawer`;

const itemClasses = `${prefixCls}-item`;

const itemTitleClasses = `${itemClasses}-title`;

const itemContentClasses = `${itemClasses}-content`;

const pickerClasses = `${prefixCls}-picker`;

const pickerPreviewClasses = `${pickerClasses}-preview`;
const pickerTextClasses = `${pickerClasses}-text`;

/* --------------------------------- states ---------------------------------------- */
const [mode, setMode] = useControlled<ThemeMode>(
props,
'mode',
props.onModeChange || noop,
'default',
);

const { circleTransition } = useCircleTransition();
const generatorRef = useRef<HTMLElement>(null);

const [mainColors, setMainColors] = useControlled<MainColor[]>(
props,
'mainColors',
props.onMainColorsChange || noop,
[],
);

/* --------------------------------- events ---------------------------------------- */

const debounceGeneratePalettes = useDebounceFn(
(mainColors: MainColor[], mode: ThemeMode = 'default') => {
const update = () => {
const reverse = mode === 'dark' ? true : false;

const palettes = generatePalettes(mainColors, reverse);

const cssCodes = toCssVariables(palettes);
document.documentElement.setAttribute('theme-mode', mode);
document.documentElement.setAttribute('theme-palette', 'custom');
appendThemeVariablesToHead(cssCodes);
};

circleTransition({
ref: generatorRef,
update,
});
},
{
wait: 300,
},
);

const handleMainColorsChange = (payload: { name: string; color: string }) => {
const { name, color } = payload;

const newColors = [...mainColors];

const target = newColors.find((item) => item.name === name);

if (target) {
target.color = color;
}

setMainColors(newColors);

debounceGeneratePalettes.run(newColors, mode);
};

const handleModeChange = (reverse: boolean) => {
const mode = reverse ? 'dark' : 'default';
setMode(mode);

debounceGeneratePalettes.run(mainColors, mode);
};

// generate current theme when component mounted
useEffect(() => {
if (!addThemeCodeOnMounted) {
return;
}
debounceGeneratePalettes.run(mainColors, mode);
debounceGeneratePalettes.flush();
}, []);

return {
classes,
styles,

drawerClasses,

itemClasses,
itemTitleClasses,
itemContentClasses,

pickerClasses,
pickerPreviewClasses,
pickerTextClasses,

mainColors,
handleMainColorsChange,
reverse: mode === 'dark',
handleModeChange,

visible,
onVisibleChange,

generatorRef,
};
};
4 changes: 4 additions & 0 deletions packages/casts-theme-generator/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './hooks';
export * from './theme-generator';
export * from './types';
export * from './utils/generate-palette';
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// @import '@casts/theme/styles/scss/core';
@import '@casts/theme/styles/scss/vars/core';

$theme-generator-prefix-cls: #{$prefix-cls}-theme-generator;

.#{$theme-generator-prefix-cls} {
max-width: 320px;

&-drawer {
.#{$prefix-cls}-drawer-overlay {
background-color: transparent;
}
}

&-item {
display: flex;
align-items: center;
justify-content: space-between;

&-title {
padding-right: $space-05-x;
}

& + & {
margin-top: $space-1-x;
}
}

&-picker {
display: inline-flex;
gap: $space-05-x;
align-items: center;
width: 128px;
padding: $space-05-x;
cursor: pointer;
border: $border-width-xsmall solid $color-border-component-default;
border-radius: $radius-medium;

&-preview {
flex-shrink: 0;
width: $size-small;
height: $size-small;
border-radius: $radius-medium;
box-shadow: $shadow-level-1;
}
}
}
Loading

0 comments on commit 308fa84

Please sign in to comment.