First Commit
This commit is contained in:
commit
3f63c202f6
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*]
|
||||||
|
indent_size = 3
|
||||||
|
indent_style = space
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
out/
|
||||||
|
node_modules/
|
24
package-lock.json
generated
Normal file
24
package-lock.json
generated
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@hibas123/theme-preact",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@hibas123/theme": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hibas123/theme/-/theme-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-bx9g8OgxaMtsgnOEh+152WRC6hE8pHNR+Q2g/K9cfIY9Uy7k6ZHrsWz2ql8HJNJ/0Tn6/a15l5wQJyqBRnLnvQ=="
|
||||||
|
},
|
||||||
|
"preact": {
|
||||||
|
"version": "10.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.3.4.tgz",
|
||||||
|
"integrity": "sha512-wMgzs/RGYf0I1PZf8ZFJdyU/3kCcwepJyVYe+N9FGajyQWarMoPrPfrQajcG0psPj6ySYv2cSuLYFCihvV/Qrw=="
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "3.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
|
||||||
|
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
package.json
Normal file
23
package.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@hibas123/theme-preact",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "out/index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"@hibas123/theme": "^2.0.2",
|
||||||
|
"preact": "^10.3.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"preact": "^10.3.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^3.8.3"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"watch": "tsc -w"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
45
src/Button.tsx
Normal file
45
src/Button.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
export enum ButtonFormats {
|
||||||
|
DEFAULT,
|
||||||
|
PRIMARY,
|
||||||
|
SECONDARY,
|
||||||
|
ERROR,
|
||||||
|
SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IButtonProps extends JSX.HTMLAttributes<HTMLButtonElement> {
|
||||||
|
format: ButtonFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Button({
|
||||||
|
children,
|
||||||
|
format,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: IButtonProps) {
|
||||||
|
let cl = "ht-btn";
|
||||||
|
|
||||||
|
if (className) cl += " " + className;
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case ButtonFormats.DEFAULT:
|
||||||
|
break;
|
||||||
|
case ButtonFormats.PRIMARY:
|
||||||
|
cl += " ht-btn-primary";
|
||||||
|
break;
|
||||||
|
case ButtonFormats.SECONDARY:
|
||||||
|
cl += " ht-btn-secondary";
|
||||||
|
break;
|
||||||
|
case ButtonFormats.ERROR:
|
||||||
|
cl += " ht-btn-error";
|
||||||
|
break;
|
||||||
|
case ButtonFormats.SUCCESS:
|
||||||
|
cl += " ht-btn-success";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
<button className={cl} {...props}>
|
||||||
|
{children}
|
||||||
|
</button>;
|
||||||
|
}
|
12
src/Card.tsx
Normal file
12
src/Card.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
export default function Card({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-card " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
12
src/Container.tsx
Normal file
12
src/Container.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
export default function Container({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-container " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
16
src/Fab.tsx
Normal file
16
src/Fab.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
export default function Fab({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
alignLeft,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLButtonElement> & { alignLeft: boolean }) {
|
||||||
|
let cl = "ht-fab " + (alignLeft ? "ht-fab-left " : "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className={cl + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
15
src/Header.tsx
Normal file
15
src/Header.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
//TODO: Implement predefined children styles
|
||||||
|
|
||||||
|
export default function Header({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<header className={"ht-header " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
12
src/IconButton.tsx
Normal file
12
src/IconButton.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
export default function IconButton({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLButtonElement>) {
|
||||||
|
return (
|
||||||
|
<button className={"ht-btn ht-btn-icon " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
81
src/Input.tsx
Normal file
81
src/Input.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
export function Input({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLInputElement>) {
|
||||||
|
return (
|
||||||
|
<input className={"ht-inp " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</input>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TextArea({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLTextAreaElement>) {
|
||||||
|
return (
|
||||||
|
<textarea className={"ht-inp " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</textarea>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputGroup({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-input-group " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IInputCheckboxProps {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
export function InputCheckbox({
|
||||||
|
label,
|
||||||
|
...props
|
||||||
|
}: IInputCheckboxProps & JSX.HTMLAttributes<HTMLInputElement>) {
|
||||||
|
return (
|
||||||
|
<label class="ht-input-checkbox">
|
||||||
|
{label}
|
||||||
|
<input type="checkbox" {...props} />
|
||||||
|
<span></span>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IInputRadioProps {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
export function InputRadio({
|
||||||
|
label,
|
||||||
|
...props
|
||||||
|
}: IInputCheckboxProps & JSX.HTMLAttributes<HTMLInputElement>) {
|
||||||
|
return (
|
||||||
|
<label class="ht-input-checkbox">
|
||||||
|
{label}
|
||||||
|
<input type="radio" {...props} />
|
||||||
|
<span></span>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputSelect({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLSelectElement>) {
|
||||||
|
return (
|
||||||
|
<select className={"ht-inp " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
27
src/List.tsx
Normal file
27
src/List.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
export interface IListProps {
|
||||||
|
clickable?: boolean;
|
||||||
|
divider?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function List({
|
||||||
|
clickable,
|
||||||
|
divider,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLUListElement> & IListProps) {
|
||||||
|
let cl = "ht-list ";
|
||||||
|
if (clickable) cl += "ht-list-clickable ";
|
||||||
|
|
||||||
|
if (divider) cl += "ht-list-divider ";
|
||||||
|
|
||||||
|
cl += className;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className={className} {...props}>
|
||||||
|
{children}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
53
src/Modal.tsx
Normal file
53
src/Modal.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
export function Modal({
|
||||||
|
hidden,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement> & { hidden?: boolean }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={"ht-modal " + className + (hidden ? "ht-modal-hidden" : "")}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalTitle({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-modal-title " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-modal-content " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalActions({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-modal-action " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
13
src/Table.tsx
Normal file
13
src/Table.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { h, JSX } from "preact";
|
||||||
|
|
||||||
|
export default function Table({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: JSX.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div className={"ht-table " + className} {...props}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
106
src/Theme.tsx
Normal file
106
src/Theme.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { h } from "preact";
|
||||||
|
import { useEffect, useState, useMemo } from "preact/hooks";
|
||||||
|
|
||||||
|
export enum ThemeModes {
|
||||||
|
AUTO,
|
||||||
|
LIGHT,
|
||||||
|
DARK
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IThemeProps {
|
||||||
|
mode: ThemeModes;
|
||||||
|
children: any;
|
||||||
|
|
||||||
|
primary?: string;
|
||||||
|
onPrimary?: string;
|
||||||
|
secondary?: string;
|
||||||
|
onSecondary?: string;
|
||||||
|
error?: string;
|
||||||
|
onError?: string;
|
||||||
|
success?: string;
|
||||||
|
onSuccess?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modeMediaQuery = window.matchMedia("prefers-color-scheme: dark");
|
||||||
|
|
||||||
|
export default function Theme({ mode, children, ...colors }: IThemeProps) {
|
||||||
|
const [isDark, setIsDark] = useState(
|
||||||
|
mode === ThemeModes.AUTO
|
||||||
|
? modeMediaQuery.matches
|
||||||
|
: mode === ThemeModes.DARK
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (mode === ThemeModes.AUTO) {
|
||||||
|
const listener = (ev: MediaQueryListEvent) => {
|
||||||
|
setIsDark(ev.matches);
|
||||||
|
};
|
||||||
|
modeMediaQuery.addListener(listener);
|
||||||
|
|
||||||
|
setIsDark(modeMediaQuery.matches);
|
||||||
|
|
||||||
|
return modeMediaQuery.removeListener(listener);
|
||||||
|
}
|
||||||
|
}, [mode]);
|
||||||
|
|
||||||
|
const style = useMemo(() => {
|
||||||
|
let style = "";
|
||||||
|
const getRGB = (color: string) => {
|
||||||
|
const cv = document.createElement("canvas");
|
||||||
|
cv.width = 10;
|
||||||
|
cv.height = 10;
|
||||||
|
|
||||||
|
const ctx = cv.getContext("2d");
|
||||||
|
ctx.rect(0, 0, 1, 1);
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
const [r, g, b] = ctx.getImageData(0, 0, 10, 10).data;
|
||||||
|
|
||||||
|
return [r, g, b];
|
||||||
|
};
|
||||||
|
|
||||||
|
const add = (name: string, value: string) =>
|
||||||
|
(style += `--${name}: ${value};`);
|
||||||
|
|
||||||
|
if (colors.primary) {
|
||||||
|
add("primary:", colors.primary);
|
||||||
|
add("primary-rgb", getRGB(colors.primary).join(", "));
|
||||||
|
}
|
||||||
|
if (colors.onPrimary) add("on-primary:", colors.onPrimary);
|
||||||
|
|
||||||
|
if (colors.secondary) {
|
||||||
|
add("secondary:", colors.secondary);
|
||||||
|
add("secondary-rgb", getRGB(colors.secondary).join(", "));
|
||||||
|
}
|
||||||
|
if (colors.onSecondary) add("on-secondary:", colors.onSecondary);
|
||||||
|
|
||||||
|
if (colors.error) {
|
||||||
|
add("error:", colors.error);
|
||||||
|
add("error-rgb", getRGB(colors.error).join(", "));
|
||||||
|
}
|
||||||
|
if (colors.onError) add("on-error:", colors.onError);
|
||||||
|
|
||||||
|
if (colors.success) {
|
||||||
|
add("success:", colors.success);
|
||||||
|
add("success-rgb", getRGB(colors.success).join(", "));
|
||||||
|
}
|
||||||
|
if (colors.onSuccess) add("on-success:", colors.onSuccess);
|
||||||
|
|
||||||
|
return style;
|
||||||
|
}, [
|
||||||
|
colors.primary,
|
||||||
|
colors.secondary,
|
||||||
|
colors.error,
|
||||||
|
colors.success,
|
||||||
|
colors.onPrimary,
|
||||||
|
colors.onSecondary,
|
||||||
|
colors.onError,
|
||||||
|
colors.onSuccess
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={isDark ? "dark-theme" : "light-theme"} style={style}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
39
src/index.ts
Normal file
39
src/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import "@hibas123/theme/out/pref.css";
|
||||||
|
|
||||||
|
import Theme, { IThemeProps, ThemeModes } from "./Theme";
|
||||||
|
import Container from "./Container";
|
||||||
|
import Button, { ButtonFormats, IButtonProps } from "./Button";
|
||||||
|
import Fab from "./Fab";
|
||||||
|
import Card from "./Card";
|
||||||
|
import Header from "./Header";
|
||||||
|
import IconButton from "./IconButton";
|
||||||
|
import List from "./List";
|
||||||
|
export { Modal, ModalActions, ModalContent, ModalTitle } from "./Modal";
|
||||||
|
import Table from "./Table";
|
||||||
|
|
||||||
|
export {
|
||||||
|
IInputCheckboxProps,
|
||||||
|
IInputRadioProps,
|
||||||
|
Input,
|
||||||
|
InputCheckbox,
|
||||||
|
InputGroup,
|
||||||
|
InputRadio,
|
||||||
|
InputSelect,
|
||||||
|
TextArea
|
||||||
|
} from "./Input";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Theme,
|
||||||
|
IThemeProps,
|
||||||
|
ThemeModes,
|
||||||
|
Container,
|
||||||
|
Button,
|
||||||
|
ButtonFormats,
|
||||||
|
IButtonProps,
|
||||||
|
Fab,
|
||||||
|
Card,
|
||||||
|
IconButton,
|
||||||
|
Header,
|
||||||
|
List,
|
||||||
|
Table
|
||||||
|
};
|
13
tsconfig.json
Normal file
13
tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"target": "ES2019",
|
||||||
|
"jsx": "react",
|
||||||
|
"jsxFactory": "h",
|
||||||
|
"outDir": "out/",
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user