diff --git a/README.md b/README.md index 26450f0..5fce381 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,10 @@ html body { // Checkbox component --w-checkbox-color: var(--w-color-primary); + // Collapsible + --w-collapsible-initial-height: 0; + --w-collapsible-max-height: 100%; + // Progress component --w-progress-color: var(--w-color-primary); --w-progress-background: var(--w-color-primary-50); @@ -197,19 +201,23 @@ import { Accordion } from 'webcoreui/react' - [Button](https://github.com/Frontendland/webcoreui/tree/main/src/components/Button) - [Card](https://github.com/Frontendland/webcoreui/tree/main/src/components/Card) - [Checkbox](https://github.com/Frontendland/webcoreui/tree/main/src/components/Checkbox) +- [Collapsible](https://github.com/Frontendland/webcoreui/tree/main/src/components/Collapsible) - [ConditionalWrapper](https://github.com/Frontendland/webcoreui/tree/main/src/components/ConditionalWrapper) - [Icon](https://github.com/Frontendland/webcoreui/tree/main/src/components/Icon) - [Input](https://github.com/Frontendland/webcoreui/tree/main/src/components/Input) - [Menu](https://github.com/Frontendland/webcoreui/tree/main/src/components/Menu) - [Modal](https://github.com/Frontendland/webcoreui/tree/main/src/components/Modal) +- [Popover](https://github.com/Frontendland/webcoreui/tree/main/src/components/Popover) - [Progress](https://github.com/Frontendland/webcoreui/tree/main/src/components/Progress) - [Radio](https://github.com/Frontendland/webcoreui/tree/main/src/components/Radio) - [Rating](https://github.com/Frontendland/webcoreui/tree/main/src/components/Rating) +- [Sheet](https://github.com/Frontendland/webcoreui/tree/main/src/components/Sheet) - [Slider](https://github.com/Frontendland/webcoreui/tree/main/src/components/Slider) - [Spinner](https://github.com/Frontendland/webcoreui/tree/main/src/components/Spinner) - [Switch](https://github.com/Frontendland/webcoreui/tree/main/src/components/Switch) - [Table](https://github.com/Frontendland/webcoreui/tree/main/src/components/Table) - [Tabs](https://github.com/Frontendland/webcoreui/tree/main/src/components/Tabs) +- [Textarea](https://github.com/Frontendland/webcoreui/tree/main/src/components/Textarea) - [ThemeSwitcher](https://github.com/Frontendland/webcoreui/tree/main/src/components/ThemeSwitcher) - [Timeline](https://github.com/Frontendland/webcoreui/blob/main/src/pages/timeline.astro) - [Toast](https://github.com/Frontendland/webcoreui/tree/main/src/components/Toast) diff --git a/package.json b/package.json index 0e33220..30482c1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "webcoreui", "type": "module", - "version": "0.1.0", + "version": "0.2.0", "scripts": { "dev": "astro dev", "build": "astro check && astro build", @@ -43,6 +43,7 @@ "react.d.ts", "react.js", "index.js", + "index.d.ts", "README.md", "LICENSE" ], diff --git a/scripts/build.js b/scripts/build.js index e2e35e5..e58d144 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -56,5 +56,6 @@ fs.writeFileSync('dist/astro.d.ts', buildTypes('astro')) fs.writeFileSync('dist/svelte.d.ts', buildTypes('svelte')) fs.writeFileSync('dist/react.d.ts', buildTypes('react')) fs.writeFileSync('dist/icons.d.ts', buildTypes('icons')) +fs.writeFileSync('dist/index.d.ts', buildTypes('utils')) console.log('✅ Package built') diff --git a/scripts/buildTypes.js b/scripts/buildTypes.js index 75eb0b2..47ecfc3 100644 --- a/scripts/buildTypes.js +++ b/scripts/buildTypes.js @@ -28,7 +28,8 @@ const buildTypes = type => { 'Input', 'Radio', 'Switch', - 'Slider' + 'Slider', + 'Textarea' ] return componentsWithSvelteSpecificTypes.includes(component) @@ -92,6 +93,80 @@ const buildTypes = type => { } `) } + + if (type === 'utils') { + return format(` + type PopoverPosition = 'top' + | 'top-start' + | 'top-end' + | 'left' + | 'left-start' + | 'left-end' + | 'right' + | 'right-start' + | 'right-end' + | 'bottom' + | 'bottom-start' + | 'bottom-end' + + type Popover = { + trigger: string + popover: string + position?: PopoverPosition + offset?: number + closeOnBlur?: boolean + } + + type Toast = { + element: string + timeout?: number + title?: string + content?: string + theme?: 'info' | 'success' | 'warning' | 'alert' | null + position?: 'bottom-left' + | 'top-left' + | 'top-right' + | 'bottom-full' + | 'top-full' + } + + declare module 'webcoreui' { + export const classNames = (classes: any[]) => string + + export const setCookie = (name: string, value: string, days: number) => {} + export const getCookie = (name: string) => string | null + export const removeCookie = (name: string) => {} + + export const debounce = (fn: any, waitFor: number) => any + + export const dispatch = (event: string, detail: any) => {} + export const listen = (event: string, callback: (e: any) => any) => { + remove() + } + + export const clamp = (num: number, min: number, max: number) => number + export const lerp = (start: number, end: number, value: number) => number + export const invlerp = (start: number, end: number, value: number) => number + export const interpolate = ( + value: number, + input: [start: number, end: number], + output: [start: number, end: number], + ) => number + + export const modal = (selector: string) => {} + export const closeModal = (selector: string) => {} + + export const popover = (config: Popover) => { + remove() + } + export const closePopover = (selector: string) => {} + + export const setDefaultTimeout = (time: number) => number + export const toast = (config: Toast | string) => {} + export const hideToast = (element: string) => {} + } + `) + } } export default buildTypes diff --git a/scripts/createComponent.js b/scripts/createComponent.js index 701ed62..450d895 100644 --- a/scripts/createComponent.js +++ b/scripts/createComponent.js @@ -120,6 +120,9 @@ const templates = { {sections.map(section => (

{section.title}

+ + {section.subTitle &&

} +
diff --git a/src/components/Alert/Alert.tsx b/src/components/Alert/Alert.tsx index 11c1ab0..f1e6f7f 100644 --- a/src/components/Alert/Alert.tsx +++ b/src/components/Alert/Alert.tsx @@ -38,7 +38,10 @@ const Alert = ({ return ( {icon && icon} - {!icon && theme &&
} + {!icon && theme &&
} (
diff --git a/src/components/Avatar/avatar.module.scss b/src/components/Avatar/avatar.module.scss index 9d2e90a..7b9c2f2 100644 --- a/src/components/Avatar/avatar.module.scss +++ b/src/components/Avatar/avatar.module.scss @@ -7,6 +7,8 @@ body { .avatar { @include border-radius(max); + object-fit: cover; + &:not(.borderless) { border: 3px solid var(--w-avatar-border); } diff --git a/src/components/Collapsible/Collapsible.astro b/src/components/Collapsible/Collapsible.astro new file mode 100644 index 0000000..1596e56 --- /dev/null +++ b/src/components/Collapsible/Collapsible.astro @@ -0,0 +1,63 @@ +--- +import type { CollapsibleProps } from './collapsible' + +import styles from './collapsible.module.scss' +import { classNames } from '../../utils/classNames' + +interface Props extends CollapsibleProps {} + +const { + initialHeight, + maxHeight, + toggled, + className, + togglesClassName +} = Astro.props + +const classes = [ + styles.collapsible, + maxHeight && styles.animated, + className +] + +const styleVariables = classNames([ + initialHeight && `--w-collapsible-initial-height: ${initialHeight};`, + maxHeight && `--w-collapsible-max-height: ${maxHeight};` +]) +--- + +
+
+ +
+
+
+
+
+
+ + diff --git a/src/components/Collapsible/Collapsible.svelte b/src/components/Collapsible/Collapsible.svelte new file mode 100644 index 0000000..e483328 --- /dev/null +++ b/src/components/Collapsible/Collapsible.svelte @@ -0,0 +1,48 @@ + + +
+
+ +
+
toggled = !toggled} + on:keyup={() => toggled = !toggled} + role="button" + tabindex={0} + class={togglesClassName} + > + {#if toggled} + + {:else} + + {/if} +
+
diff --git a/src/components/Collapsible/Collapsible.tsx b/src/components/Collapsible/Collapsible.tsx new file mode 100644 index 0000000..89318f6 --- /dev/null +++ b/src/components/Collapsible/Collapsible.tsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react' +import type { ReactCollapsibleProps } from './collapsible' + +import styles from './collapsible.module.scss' +import { classNames } from '../../utils/classNames' + +const Collapsible = ({ + initialHeight, + maxHeight, + toggled, + on, + off, + children, + className, + togglesClassName +}: ReactCollapsibleProps) => { + const [toggle, setToggled] = useState(toggled) + + const classes = classNames([ + styles.collapsible, + maxHeight && styles.animated, + className + ]) + + const styleVariables = { + ...(initialHeight && { '--w-collapsible-initial-height': initialHeight }), + ...(maxHeight && { '--w-collapsible-max-height': maxHeight }) + } as React.CSSProperties + + return ( +
+
+ {children} +
+
setToggled(!toggle)} + onKeyUp={() => setToggled(!toggle)} + role="button" + tabIndex={0} + className={togglesClassName} + > + {toggle ? off : on} +
+
+ ) +} + +export default Collapsible diff --git a/src/components/Collapsible/collapsible.module.scss b/src/components/Collapsible/collapsible.module.scss new file mode 100644 index 0000000..3c5c470 --- /dev/null +++ b/src/components/Collapsible/collapsible.module.scss @@ -0,0 +1,29 @@ +@import '../../scss/config.scss'; + +body { + --w-collapsible-initial-height: 0; + --w-collapsible-max-height: 100%; +} + +.collapsible { + @include layout(flex, column, xs); + + &:not([data-toggled="true"]) [data-toggle-off], + &[data-toggled="true"] [data-toggle-on] { + @include visibility(none); + } + + &[data-toggled="true"] .wrapper { + max-height: var(--w-collapsible-max-height); + } + + &.animated .wrapper { + @include transition(max-height, .5s); + } + + .wrapper { + @include visibility(hidden); + + max-height: var(--w-collapsible-initial-height); + } +} diff --git a/src/components/Collapsible/collapsible.ts b/src/components/Collapsible/collapsible.ts new file mode 100644 index 0000000..e392af0 --- /dev/null +++ b/src/components/Collapsible/collapsible.ts @@ -0,0 +1,14 @@ +export type CollapsibleProps = { + initialHeight?: string + maxHeight?: string + toggled?: boolean + className?: string + togglesClassName?: string +} + +export type ReactCollapsibleProps = { + on: React.ReactNode + off: React.ReactNode + children?: React.ReactNode +} & CollapsibleProps + diff --git a/src/components/Modal/modal.module.scss b/src/components/Modal/modal.module.scss index d799e4c..18ce28c 100644 --- a/src/components/Modal/modal.module.scss +++ b/src/components/Modal/modal.module.scss @@ -3,7 +3,7 @@ .modal { @include transition(); @include position(fixed, 't50%', 'l50%'); - @include spacing(0, p-default); + @include spacing(m0, p-default); @include layer(modal); @include visibility(block, 0); @include border(primary-50); diff --git a/src/components/Popover/Popover.astro b/src/components/Popover/Popover.astro new file mode 100644 index 0000000..3d0f663 --- /dev/null +++ b/src/components/Popover/Popover.astro @@ -0,0 +1,23 @@ +--- +import type { PopoverProps } from './popover' +import styles from './popover.module.scss' + +interface Props extends PopoverProps {} + +const { + id, + className +} = Astro.props + +const classes = [ + styles.popover, + className +] +--- + + + + diff --git a/src/components/Popover/Popover.svelte b/src/components/Popover/Popover.svelte new file mode 100644 index 0000000..51a2a22 --- /dev/null +++ b/src/components/Popover/Popover.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx new file mode 100644 index 0000000..8974961 --- /dev/null +++ b/src/components/Popover/Popover.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import type { ReactPopoverProps } from './popover' + +import styles from './popover.module.scss' +import { classNames } from '../../utils/classNames' + +const Popover = ({ + id, + className, + children +}: ReactPopoverProps) => { + const classes = classNames([ + styles.popover, + className + ]) + + return ( + + {children} + + ) +} + +export default Popover diff --git a/src/components/Popover/popover.module.scss b/src/components/Popover/popover.module.scss new file mode 100644 index 0000000..5871255 --- /dev/null +++ b/src/components/Popover/popover.module.scss @@ -0,0 +1,51 @@ +@import '../../scss/config.scss'; + +.popover { + @include spacing(m0, p-default); + @include layer(modal); + @include visibility(block, 0); + @include border(primary-50); + @include background(primary-70); + @include typography(primary); + @include border-radius(md); + @include size(w300px); + + transform: translateY(-5px); + pointer-events: none; + + &[data-show] { + @include transition(); + } + + &[data-show="true"] { + @include visibility(1); + + transform: translateY(0); + pointer-events: all; + } + + &[data-position="top"] { + transform: translateY(5px); + + &[data-show="true"] { + transform: translate(0); + } + } + + &[data-position="left"] { + transform: translateY(0) translateX(5px); + + &[data-show="true"] { + transform: translate(0); + } + } + + + &[data-position="right"] { + transform: translateY(0) translateX(-5px); + + &[data-show="true"] { + transform: translate(0); + } + } +} diff --git a/src/components/Popover/popover.ts b/src/components/Popover/popover.ts new file mode 100644 index 0000000..ee57ac6 --- /dev/null +++ b/src/components/Popover/popover.ts @@ -0,0 +1,8 @@ +export type PopoverProps = { + id?: string + className?: string +} + +export type ReactPopoverProps = { + children?: React.ReactNode +} & PopoverProps diff --git a/src/components/Sheet/Sheet.astro b/src/components/Sheet/Sheet.astro new file mode 100644 index 0000000..8d86743 --- /dev/null +++ b/src/components/Sheet/Sheet.astro @@ -0,0 +1,29 @@ +--- +import type { SheetProps } from './sheet' + +import Modal from '../Modal/Modal.astro' + +import styles from './sheet.module.scss' +import { classNames } from '../../utils/classNames' + +interface Props extends SheetProps {} + +const { + position, + className, + ...rest +} = Astro.props + +const classes = classNames([ + styles.sheet, + position && styles[position], + className +]) +--- + + + + diff --git a/src/components/Sheet/Sheet.svelte b/src/components/Sheet/Sheet.svelte new file mode 100644 index 0000000..cae5c3f --- /dev/null +++ b/src/components/Sheet/Sheet.svelte @@ -0,0 +1,24 @@ + + + + + diff --git a/src/components/Sheet/Sheet.tsx b/src/components/Sheet/Sheet.tsx new file mode 100644 index 0000000..0cbe7d6 --- /dev/null +++ b/src/components/Sheet/Sheet.tsx @@ -0,0 +1,32 @@ +import React from 'react' +import type { ReactSheetProps } from './sheet' + +import Modal from '../Modal/Modal.tsx' + +import styles from './sheet.module.scss' +import { classNames } from '../../utils/classNames' + +const Sheet = ({ + position, + className, + children, + ...rest +}: ReactSheetProps) => { + const classes = classNames([ + styles.sheet, + position && styles[position], + className + ]) + + return ( + + {children} + + + ) +} + +export default Sheet diff --git a/src/components/Sheet/sheet.module.scss b/src/components/Sheet/sheet.module.scss new file mode 100644 index 0000000..05572ab --- /dev/null +++ b/src/components/Sheet/sheet.module.scss @@ -0,0 +1,68 @@ +@import '../../scss/config.scss'; + +.sheet { + @include position(t0, 'r-100%', lauto); + @include visibility(1); + @include border-radius(none); + @include size(100%); + @include border(0); + @include border(left, primary-50); + + transform: none; + + &[data-show="true"] { + @include position(r0); + + transform: none; + } + + &.left { + @include position('l-100%', rauto); + @include border(0); + @include border(right, primary-50); + + &[data-show="true"] { + @include position(l0); + } + } + + &.top { + @include position(t0, l0); + @include size(hauto); + @include border(0); + @include border(bottom, primary-50); + + transform: translateY(-100%); + min-width: 100%; + + &[data-show="true"] { + transform: translateY(0); + } + } + + &.bottom { + @include position(b0, l0); + @include size(hauto); + @include border(0); + @include border(top, primary-50); + + transform: translateY(100%); + top: auto; + min-width: 100%; + + &[data-show="true"] { + transform: translateY(0); + } + } +} + +@include media(xs) { + .sheet { + @include position(r-500px); + @include size(w500px); + + &.left { + @include position(l-500px); + } + } +} diff --git a/src/components/Sheet/sheet.ts b/src/components/Sheet/sheet.ts new file mode 100644 index 0000000..1a4ab31 --- /dev/null +++ b/src/components/Sheet/sheet.ts @@ -0,0 +1,10 @@ +import type { ModalProps, ReactModalProps } from '../Modal/modal' + +export type SheetProps = { + position?: 'left' + | 'top' + | 'bottom' + | null +} & ModalProps + +export type ReactSheetProps = SheetProps & ReactModalProps diff --git a/src/components/Textarea/Textarea.astro b/src/components/Textarea/Textarea.astro new file mode 100644 index 0000000..6a1b8d5 --- /dev/null +++ b/src/components/Textarea/Textarea.astro @@ -0,0 +1,44 @@ +--- +import type { TextareaProps } from './textarea' +import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro' + +import styles from './textarea.module.scss' + +interface Props extends TextareaProps {} + +const { + label, + placeholder, + subText, + value = '', + disabled, + className +} = Astro.props + +const classes = [ + styles.textarea, + className +] + +const useLabel = !!(label || subText) +--- + + + + + + diff --git a/src/components/Textarea/Textarea.svelte b/src/components/Textarea/Textarea.svelte new file mode 100644 index 0000000..5057daf --- /dev/null +++ b/src/components/Textarea/Textarea.svelte @@ -0,0 +1,45 @@ + + + + {#if label} +
{label}
+ {/if} + + {#if subText} +
+ {@html subText} +
+ {/if} +
diff --git a/src/components/Textarea/Textarea.tsx b/src/components/Textarea/Textarea.tsx new file mode 100644 index 0000000..626d806 --- /dev/null +++ b/src/components/Textarea/Textarea.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import type { ReactTextareaProps } from './textarea' +import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx' + +import styles from './textarea.module.scss' +import { classNames } from '../../utils/classNames' + +const Textarea = ({ + label, + placeholder, + subText, + value = '', + disabled, + className, + ...rest +}: ReactTextareaProps) => { + const classes = classNames([ + styles.textarea, + className + ]) + + const useLabel = !!(label || subText) + + return ( + ( +