diff --git a/README.md b/README.md index 9627883..57f5fb9 100644 --- a/README.md +++ b/README.md @@ -144,11 +144,13 @@ import { Accordion } from 'webcoreui/react' - [Checkbox](https://github.com/Frontendland/webcoreui/tree/main/src/components/Checkbox) - [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) - [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) - [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) - [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/scripts/buildTypes.js b/scripts/buildTypes.js index a76389a..b3e94e6 100644 --- a/scripts/buildTypes.js +++ b/scripts/buildTypes.js @@ -41,6 +41,7 @@ const buildTypes = type => { 'Icon', 'Rating', 'Spinner', + 'Table', 'TimelineItem', 'Progress' ] diff --git a/scripts/createComponent.js b/scripts/createComponent.js index 0216063..7db50aa 100644 --- a/scripts/createComponent.js +++ b/scripts/createComponent.js @@ -91,20 +91,12 @@ const templates = { import Svelte${component} from '@components/${component}/${component}.svelte' import React${component} from '@components/${component}/${component}.tsx' - const sections = [ - { - title: 'Astro ${lowerCaseComponent}s', - component: Astro${component} - }, - { - title: 'Svelte ${lowerCaseComponent}s', - component: Svelte${component} - }, - { - title: 'React ${lowerCaseComponent}s', - component: React${component} - } - ] + import { getSections } from '@helpers' + + const sections = getSections({ + title: '${lowerCaseComponent}s', + components: [Astro${component}, Svelte${component}, React${component}] + }) --- diff --git a/src/components/Input/Input.astro b/src/components/Input/Input.astro new file mode 100644 index 0000000..b93bffb --- /dev/null +++ b/src/components/Input/Input.astro @@ -0,0 +1,52 @@ +--- +import type { InputProps } from './input' +import ConditionalWrapper from '@components/ConditionalWrapper/ConditionalWrapper.astro' + +import './input.scss' + +interface Props extends InputProps {} + +const { + type = 'text', + theme, + label, + subText, + fill, + className, + ...rest +} = Astro.props + +const classes = [ + 'w-input', + theme, + fill && 'fill', + className +] + +const useLabel = !!(label || subText || Astro.slots.has('icon')) +--- + + + + + diff --git a/src/components/Input/Input.svelte b/src/components/Input/Input.svelte new file mode 100644 index 0000000..4f1caa0 --- /dev/null +++ b/src/components/Input/Input.svelte @@ -0,0 +1,52 @@ + + + + {#if label} +
{label}
+ {/if} + + + + + {#if label} +
+ {@html subText} +
+ {/if} +
diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx new file mode 100644 index 0000000..ee0b406 --- /dev/null +++ b/src/components/Input/Input.tsx @@ -0,0 +1,59 @@ +import React from 'react' +import type { ReactInputProps } from './input' +import ConditionalWrapper from '@components/ConditionalWrapper/ConditionalWrapper.tsx' + +import './input.scss' + +const Input = ({ + type = 'text', + theme, + label, + subText, + fill, + icon, + value, + className, + ...rest +}: ReactInputProps) => { + const classes = [ + 'w-input', + theme, + fill && 'fill', + className + ].filter(Boolean).join(' ') + + const useLabel = !!(label || subText || icon) + + return ( + ( + + )}> + {label && ( +
{label}
+ )} + ( +
+ {children} +
+ )}> + {icon && icon} + +
+ {subText && ( +
+ )} + + ) +} + +export default Input diff --git a/src/components/Input/input.scss b/src/components/Input/input.scss new file mode 100644 index 0000000..e34fbe5 --- /dev/null +++ b/src/components/Input/input.scss @@ -0,0 +1,83 @@ +@import '../../scss/config.scss'; + +.w-input { + border-radius: 2px; + padding: 5px 10px; + border: 1px solid #252525; + background: transparent; + font-family: Regular; + color: #FFF; + line-height: 20px; + width: 100%; + color-scheme: dark; + + &[disabled] { + cursor: no-drop; + color: #555; + } + + &::file-selector-button { + background: transparent; + border: 0; + color: #FFF; + } + + &[type="color"] { + padding: 0; + } + + &.info { + border-color: #48dbfb; + } + + &.success { + border-color: #1dd1a1; + } + + &.warning { + border-color: #f7aa61; + } + + &.alert { + border-color: #ee5253; + } + + &.fill { + background: #252525; + } +} + +.w-input-label { + display: flex; + flex-direction: column; + + .label { + font-size: 16px; + color: #BBB; + margin-bottom: 5px; + } + + .input-wrapper { + display: flex; + gap: 10px; + align-items: center; + position: relative; + + input { + padding-left: 40px; + } + + svg { + position: absolute; + left: 10px; + width: 20px; + height: 20px; + } + } + + .subtext { + font-size: 14px; + color: #555; + margin-top: 5px; + } +} diff --git a/src/components/Input/input.ts b/src/components/Input/input.ts new file mode 100644 index 0000000..e3df34d --- /dev/null +++ b/src/components/Input/input.ts @@ -0,0 +1,44 @@ +export type InputProps = { + type?: 'text' + | 'email' + | 'password' + | 'number' + | 'tel' + | 'url' + | 'search' + | 'file' + | 'date' + | 'datetime-local' + | 'month' + | 'week' + | 'time' + | 'color' + theme?: 'info' + | 'success' + | 'warning' + | 'alert' + | null + value?: string | number + name?: string + placeholder?: string + label?: string + disabled?: boolean + subText?: string + fill?: boolean + maxLength?: number + min?: number + max?: number + step?: number + multiple?: boolean + pattern?: string + required?: boolean + autofocus?: boolean + autocomplete?: 'on' | 'off' + className?: string + [key: string]: any +} + +export type ReactInputProps = { + icon?: string + children?: React.ReactNode +} & InputProps diff --git a/src/components/Table/Table.astro b/src/components/Table/Table.astro new file mode 100644 index 0000000..08e62a5 --- /dev/null +++ b/src/components/Table/Table.astro @@ -0,0 +1,60 @@ +--- +import type { TableProps } from './table' +import './table.scss' + +interface Props extends TableProps {} + +const { + headings, + footer, + data, + hover, + striped, + offsetStripe, + compact, + className +} = Astro.props + +const classes = [ + 'w-table', + hover && 'hover', + striped && `striped-${striped}s`, + offsetStripe && 'offset', + compact && 'compact', + className +] +--- + +
+ + {headings?.length && ( + + + {headings.map(heading => ( + + ))} + + + )} + + {data.map(row => ( + + {row.map(column => ( + + ))} + + ))} + + {footer?.length && ( + + + {footer.map(data => ( + + ))} + + + )} +
{heading}
+ +
{data}
+
diff --git a/src/components/Table/Table.svelte b/src/components/Table/Table.svelte new file mode 100644 index 0000000..20ea62d --- /dev/null +++ b/src/components/Table/Table.svelte @@ -0,0 +1,54 @@ + + +
+ + {#if headings?.length} + + + {#each headings as heading} + + {/each} + + + {/if} + + {#each data as row} + + {#each row as column} + + {/each} + + {/each} + + {#if footer?.length} + + + {#each footer as data} + + {/each} + + + {/if} +
{heading}
{@html column}
{data}
+
diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx new file mode 100644 index 0000000..301a5c8 --- /dev/null +++ b/src/components/Table/Table.tsx @@ -0,0 +1,63 @@ +import React from 'react' +import type { TableProps } from './table' + +import './table.scss' + +const Table = ({ + headings, + footer, + data, + hover, + striped, + offsetStripe, + compact, + className +}: TableProps) => { + const classes = [ + 'w-table', + hover && 'hover', + striped && `striped-${striped}s`, + offsetStripe && 'offset', + compact && 'compact', + className + ].filter(Boolean).join(' ') + + return ( +
+ + {headings?.length && ( + + + {headings.map((heading, index) => ( + + ))} + + + )} + + {data.map((row, rowIndex) => ( + + {row.map((column, columnIndex) => ( + + ))} + + {footer?.length && ( + + + {footer.map((data, index) => ( + + ))} + + + )} +
{heading}
+ ))} +
{data}
+
+ ) +} + +export default Table diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss new file mode 100644 index 0000000..cf6384d --- /dev/null +++ b/src/components/Table/table.scss @@ -0,0 +1,65 @@ +@import '../../scss/config.scss'; + +.w-table { + overflow-x: auto; + + table { + width: 100%; + border-collapse: collapse; + text-align: left; + font-size: 16px; + } + + thead, + tfoot { + font-family: Bold; + } + + th, + td { + padding: 5px 10px; + white-space: nowrap; + } + + thead, + tr { + border-bottom: 1px solid #252525; + + &:last-child { + border-bottom: 0; + } + } + + a { + text-decoration: underline; + } + + tfoot, + &.hover tr:hover, + &.striped-rows tbody tr:nth-child(odd), + &.striped-rows.offset tbody tr:nth-child(even), + &.striped-columns td:nth-child(odd), + &.striped-columns.offset td:nth-child(even) { + background: #111; + } + + &.striped-rows tr, + &.striped-rows thead, + &.striped-columns tr, + &.striped-columns thead { + border-bottom: 0; + } + + &.striped-rows.offset tbody tr:nth-child(odd), + &.striped-rows.offset tfoot, + &.striped-columns.offset td:nth-child(odd), + &.striped-columns tfoot { + background: transparent; + } + + &.compact { + th, td { + padding: 3px 10px; + } + } +} diff --git a/src/components/Table/table.ts b/src/components/Table/table.ts new file mode 100644 index 0000000..947680b --- /dev/null +++ b/src/components/Table/table.ts @@ -0,0 +1,10 @@ +export type TableProps = { + headings?: string[] + footer?: string[] + data: string[][] + hover?: boolean + striped?: 'column' | 'row' | null + offsetStripe?: boolean + compact?: boolean + className?: string +} diff --git a/src/helpers.js b/src/helpers.js new file mode 100644 index 0000000..bafd477 --- /dev/null +++ b/src/helpers.js @@ -0,0 +1,26 @@ +export const getSections = ({ + title, + components, + showSubTitle = false +}) => { + return [ + { + title: `Astro ${title}`, + component: components[0], + }, + { + title: `Svelte ${title}`, + component: components[1], + ...(showSubTitle && { + subTitle: 'For interactive examples, visit Svelte Playground', + }) + }, + { + title: `React ${title}`, + component: components[2], + ...(showSubTitle && { + subTitle: 'For interactive examples, visit React Playground', + }) + } + ] +} diff --git a/src/pages/alert.astro b/src/pages/alert.astro index 39f59d8..3a9079f 100644 --- a/src/pages/alert.astro +++ b/src/pages/alert.astro @@ -6,20 +6,12 @@ import AstroAlert from '@components/Alert/Alert.astro' import SvelteAlert from '@components/Alert/Alert.svelte' import ReactAlert from '@components/Alert/Alert.tsx' -const sections = [ - { - title: 'Astro alerts', - component: AstroAlert - }, - { - title: 'Svelte alerts', - component: SvelteAlert - }, - { - title: 'React alerts', - component: ReactAlert - } -] +import { getSections } from '@helpers' + +const sections = getSections({ + title: 'alerts', + components: [AstroAlert, SvelteAlert, ReactAlert] +}) --- diff --git a/src/pages/index.astro b/src/pages/index.astro index d949453..0a04afb 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -9,11 +9,13 @@ import Badge from '@components/Badge/Badge.astro' import Button from '@components/Button/Button.astro' import Checkbox from '@components/Checkbox/Checkbox.astro' import Icon from '@components/Icon/Icon.astro' +import Input from '@components/Input/Input.astro' import Progress from '@components/Progress/Progress.astro' import Radio from '@components/Radio/Radio.astro' import Rating from '@components/Rating/Rating.astro' import Spinner from '@components/Spinner/Spinner.astro' import Switch from '@components/Switch/Switch.astro' +import Table from '@components/Table/Table.astro' import Tabs from '@components/Tabs/Tabs.astro' import Timeline from '@components/Timeline/Timeline.astro' import TimelineItem from '@components/TimelineItem/TimelineItem.astro' @@ -111,6 +113,9 @@ const tabItems = [{ color="#f2c262" /> + + + @@ -129,6 +134,12 @@ const tabItems = [{ + + + diff --git a/src/pages/input.astro b/src/pages/input.astro new file mode 100644 index 0000000..9338d16 --- /dev/null +++ b/src/pages/input.astro @@ -0,0 +1,168 @@ +--- +import Layout from '@static/Layout.astro' +import ComponentWrapper from '@static/ComponentWrapper.astro' + +import Icon from '@components/Icon/Icon.astro' +import AstroInput from '@components/Input/Input.astro' +import SvelteInput from '@components/Input/Input.svelte' +import ReactInput from '@components/Input/Input.tsx' + +import { getSections } from '@helpers' + +const sections = getSections({ + title: 'inputs', + components: [AstroInput, SvelteInput, ReactInput], + showSubTitle: true +}) +--- + + +

Input

+
+ + + + + + + + + + + +
+ + {sections.map(section => ( +

{section.title}

+ + {section.subTitle &&

} + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ))} + diff --git a/src/pages/progress.astro b/src/pages/progress.astro index 16bfe96..bbc6bff 100644 --- a/src/pages/progress.astro +++ b/src/pages/progress.astro @@ -6,22 +6,13 @@ import AstroProgress from '@components/Progress/Progress.astro' import SvelteProgress from '@components/Progress/Progress.svelte' import ReactProgress from '@components/Progress/Progress.tsx' -const sections = [ - { - title: 'Astro progress', - component: AstroProgress, - }, - { - title: 'Svelte progress', - component: SvelteProgress, - subTitle: 'For interactive examples, visit Svelte Playground', - }, - { - title: 'React progress', - component: ReactProgress, - subTitle: 'For interactive examples, visit React Playground', - } -] +import { getSections } from '@helpers' + +const sections = getSections({ + title: 'progress', + components: [AstroProgress, SvelteProgress, ReactProgress], + showSubTitle: true +}) --- diff --git a/src/pages/table.astro b/src/pages/table.astro new file mode 100644 index 0000000..9a6638a --- /dev/null +++ b/src/pages/table.astro @@ -0,0 +1,161 @@ +--- +import Layout from '@static/Layout.astro' +import ComponentWrapper from '@static/ComponentWrapper.astro' + +import AstroTable from '@components/Table/Table.astro' +import SvelteTable from '@components/Table/Table.svelte' +import ReactTable from '@components/Table/Table.tsx' + +import { getSections } from '@helpers' + +const sections = getSections({ + title: 'tables', + components: [AstroTable, SvelteTable, ReactTable] +}) + +const tableData = [ + ['1', 'John Doe'], + ['2', 'Jane Smith'], + ['3', 'Emily Davis'], + ['4', 'Frank Williams'] +] + +const tableWithHeading = { + headings: ['ID', 'Name'], + data: tableData +} + +const tableWithHeadingAndFooter = { + headings: ['ID', 'Name'], + data: tableData, + footer: ['Total', '4'] +} + +const tableWithMultipleColumns = { + headings: ['ID', 'Name', 'Active'], + data: tableData.map(data => [...data, '✅']), + footer: ['Total', '', '4'] +} + +const overflowTable = { + headings: ['ID', 'Name', 'Email', 'Active'], + footer: ['Total', '', '', '4'], + data: tableData.map(data => [ + ...data, + `${data[1]}@example.com`, + '✅' + ]), +} +--- + + +

Table

+
+ + + + + + + + + + + +
+ + {sections.map(section => ( +

{section.title}

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ))} +
+ + diff --git a/src/playground/ReactPlayground.tsx b/src/playground/ReactPlayground.tsx index 5ca51fb..2b6e05d 100644 --- a/src/playground/ReactPlayground.tsx +++ b/src/playground/ReactPlayground.tsx @@ -6,6 +6,7 @@ import Accordion from '@components/Accordion/Accordion.tsx' import Badge from '@components/Badge/Badge.tsx' import Button from '@components/Button/Button.tsx' import Checkbox from '@components/Checkbox/Checkbox.tsx' +import Input from '@components/Input/Input.tsx' import Progress from '@components/Progress/Progress.tsx' import Radio from '@components/Radio/Radio.tsx' import Switch from '@components/Switch/Switch.tsx' @@ -17,13 +18,12 @@ import { toast } from '@utils/toast' import styles from './playground.module.scss' -console.log(styles) - const ReactPlayground = () => { const [progress, setProgress] = useState(33) const [checkbox, setCheckbox] = useState(false) const [radio, setRadio] = useState('') const [toggle, setToggle] = useState(false) + const [input, setInput] = useState('') return (
@@ -56,6 +56,21 @@ const ReactPlayground = () => { {`${checkbox}`} + + setInput(e.target.value)} + /> + + setInput(e.target.value)} + /> + + {input} + + diff --git a/src/playground/SveltePlayground.svelte b/src/playground/SveltePlayground.svelte index cd331ee..4da7f96 100644 --- a/src/playground/SveltePlayground.svelte +++ b/src/playground/SveltePlayground.svelte @@ -5,6 +5,7 @@ import Badge from '@components/Badge/Badge.svelte' import Button from '@components/Button/Button.svelte' import Checkbox from '@components/Checkbox/Checkbox.svelte' + import Input from '@components/Input/Input.svelte' import Progress from '@components/Progress/Progress.svelte' import Radio from '@components/Radio/Radio.svelte' import Switch from '@components/Switch/Switch.svelte' @@ -20,6 +21,7 @@ let checkbox = false let radio = '' let toggle = false + let input = ''
@@ -52,6 +54,21 @@ {checkbox} + + input = e.target.value} + /> + + input = e.target.value} + /> + + {input} + + diff --git a/tsconfig.json b/tsconfig.json index 12f1aaf..9b32344 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,8 @@ "@static/*": ["src/static/*"], "@playground/*": ["src/playground/*"], "@utils/*": ["src/utils/*"], - "@data": ["src/data.js"] + "@data": ["src/data.js"], + "@helpers": ["src/helpers.js"] } } }