Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ Changes proposed in this pull request:
-
-
-

Bumped version to: v0.0.0
2 changes: 0 additions & 2 deletions .github/workflows/nodejsci.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Node.js CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

Expand Down
26 changes: 6 additions & 20 deletions docs/COMPONENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,21 @@ Tabs provide a tabbed navigation. Use `Tab` elements as children of `Tabs`.

`Tabs` Properties:

- **separator** - ReactNode (optional)
- **selectedId** - string (optional)
- **children** - one or more `Tab` elements (required)
- **onChange** - `(newId: string, oldId?: string) => void` (optional)

`Tabs.Tab` Properties:

- **id** - string (required)
- **title** - ReactNode (required)
- **children** - ReactNode (required)
- **title** - string (required)
- **active** - boolean (required)
- **onClick** - `() => void` (required)

Example:

```tsx
import { Tabs } from '@maskingtech/designsystem';

<Tabs selectedId='tab1' separator={<hr />} onChange={(n,o)=>console.log(n,o)}>
<Tabs.Tab id='tab1' title='First'>First content</Tabs.Tab>
<Tabs.Tab id='tab2' title='Second'>Second content</Tabs.Tab>
<Tabs onChange={(n,o)=>console.log(n,o)}>
<Tabs.Tab title='First' active={true} onClick={() => console.log('First clicked')} />
<Tabs.Tab title='Second' active={false} onClick={() => console.log('Second clicked')} />
</Tabs>
```

Customization options (selector: `.tabs`):

- `--margin` (default: `var(--margin-container)`)
- `--foreground-color` (default: `var(--color-primary-foreground)`)
- `--font-size-nav` (default: `1.1em`)
- `--background-color-nav-item` (default: `transparent`)
- `--background-color-nav-item-active` (default: `var(--background-color-nav-item)`)
- `--background-color-nav-item-hover` (default: `var(--color-background-hover)`)
- `--padding-nav-item` (default: `0.5em 1em`)
- `--margin-separator` (default: `0.2em 0 1em 0`)
1 change: 1 addition & 0 deletions docs/CUSTOMIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import '/path/to/customizations.css';

### Input properties

- `--input-background-hover-color` (default: `rgba(0, 0, 0, 0.05)`)
- `--input-border-style` (default: `solid`)
- `--input-border-color` (default: `var(--color-border)`)
- `--input-border-small` (default: `0.05em`)
Expand Down
12 changes: 8 additions & 4 deletions docs/ELEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,10 +413,12 @@ Customization options (selector: `.button`):

### ClickArea

A wrapper element that handles clicks and optional padding.
A wrapper element that handles clicks and optional alignment, hover effect and padding.

Properties:

- **alignX** - `left` | `center` | `right` (optional, default `left`)
- **effect** - `none` | `hover` (optional, default `none`)
- **padding** - `none` | `small` | `medium` | `large` (optional, default `none`)
- **onClick** - `() => void` (optional)
- **children** - ReactNode (optional)
Expand All @@ -431,10 +433,11 @@ import { ClickArea } from '@maskingtech/designsystem';

Customization options (selector: `.clickarea`):

- `--background-hover-color` (default `var(--input-background-hover-color)`)
- `--margin` (default: `0`)
- `--padding-small` (default: `var(--container-padding-small)`)
- `--padding-medium` (default: `var(--container-padding-medium)`)
- `--padding-large` (default: `var(--container-padding-large)`)
- `--padding-small` (default: `var(--input-padding-small)`)
- `--padding-medium` (default: `var(--input-padding-medium)`)
- `--padding-large` (default: `var(--input-padding-large)`)

### Link

Expand Down Expand Up @@ -610,6 +613,7 @@ Properties:
- **value** - string (optional)
- **border** - `small` | `medium` | `large` (optional, default `small`)
- **size** - `small` | `medium` | `large` (optional, default `medium`)
- **cols** - number (optional)
- **rows** - number (optional)
- **limit** - number (optional)
- **onChange** - `ChangeEventHandler<HTMLTextAreaElement>` (optional)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@maskingtech/designsystem",
"private": false,
"version": "0.0.4",
"version": "0.0.5",
"type": "module",
"repository": {
"url": "https://github.com/MaskingTechnology/designsystem"
Expand Down
2 changes: 2 additions & 0 deletions src/DesignSystem.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
--container-padding-medium: 1.5em;
--container-padding-large: 2em;

--input-background-hover-color: rgba(0, 0, 0, 0.05);

--input-border-style: solid;
--input-border-color: var(--color-border);

Expand Down
18 changes: 10 additions & 8 deletions src/components/tabs/Tab.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@

import type { ReactNode } from 'react';
import { ClickArea, Column, Text } from '../../index';

export type Props = {
readonly id: string;
readonly title: ReactNode;
readonly children: ReactNode;
readonly title: string;
readonly active: boolean;
readonly onClick: () => void;
};

export default function Tab({ children }: Props)
export default function Tab({ title, active, onClick }: Props)
{
return <div className='tab'>
{children}
</div>;
return <Column alignX='stretch'>
<ClickArea padding='medium' effect='hover' alignX='center' onClick={onClick}>
<Text value={title} weight={active ? 'bold' : 'normal'} size='large' />
</ClickArea>
</Column>;
}
61 changes: 0 additions & 61 deletions src/components/tabs/Tabs.css

This file was deleted.

88 changes: 11 additions & 77 deletions src/components/tabs/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,23 @@

import type { ReactElement, ReactNode } from 'react';
import { useEffect, useEffectEvent, useMemo, useState } from 'react';
import type { ReactElement } from 'react';

import { Column, Row, Ruler } from '../../index';

import Tab from './Tab';
import type { Props as TabProps } from './Tab';

import './Tabs.css';

type Props = {
readonly separator?: ReactNode;
readonly selectedId?: string;
readonly children: ReactElement<TabProps> | ReactElement<TabProps>[];
readonly onChange?: (newId: string, oldId?: string) => void;
readonly children?: ReactElement<TabProps> | ReactElement<TabProps>[];
};

export function Tabs({ separator, selectedId, children, onChange }: Props)
export function Tabs({ children }: Props)
{
const [selected, setSelected] = useState<string>(selectedId ?? '');

const tabList = useMemo(() =>
{
return Array.isArray(children) ? children : [children];

}, [children]);

const tabMap = useMemo(() =>
{
const tabs: Record<string, ReactElement> = {};

tabList.forEach((element) =>
{
const tabId = element.props.id;
tabs[tabId] = element;
});

return tabs;
}, [tabList]);

const updateSelected = useEffectEvent((selectedId?: string) =>
{
const firstTabId = tabList[0]?.props.id;

setSelected(selectedId ?? firstTabId);
});

useEffect(() =>
{
updateSelected(selectedId);
}, [selectedId, tabList]);

const handleChange = (tabId: string) =>
{
if (onChange !== undefined)
{
onChange(tabId, selected);
}

setSelected(tabId);
};

return <div className='tabs'>
<div className='nav'>
{
tabList.map(element =>
{
const tabId = element.props.id;
const key = `tab-${tabId}`;
const style = tabId === selected ? 'active' : 'inactive';
const handleClick = () => handleChange(element.props.id);

return (
<div key={key} className={'item ' + style} onClick={handleClick}>
{element.props.title}
</div>
);
})
}
</div>
{
separator !== undefined
? <div className='separator'>{separator}</div>
: null
}
<div className="content">{tabMap[selected]}</div>
</div>;
return <Column alignX='stretch' gap='none'>
<Row alignX='justify'>
{children}
</Row>
<Ruler direction='horizontal' size='small' />
</Column>;
}

Tabs.Tab = Tab;
31 changes: 27 additions & 4 deletions src/elements/clickarea/ClickArea.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,39 @@
{
.clickarea
{
--background-hover-color: var(--input-background-hover-color);

--margin: 0;

--padding-small: var(--container-padding-small);
--padding-medium: var(--container-padding-medium);
--padding-large: var(--container-padding-large);
--padding-small: var(--input-padding-small);
--padding-medium: var(--input-padding-medium);
--padding-large: var(--input-padding-large);

cursor: pointer;
display: inline-block;
display: flex;
flex-direction: column;
margin: var(--margin);

&.align-x-left
{
align-items: flex-start;
}

&.align-x-center
{
align-items: center;
}

&.align-x-right
{
align-items: flex-end;
}

&.effect-hover:hover
{
background-color: var(--background-hover-color);
}

&.padding-none
{
padding: 0;
Expand Down
8 changes: 6 additions & 2 deletions src/elements/clickarea/ClickArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import type { ReactNode } from 'react';
import './ClickArea.css';

type Props = {
readonly alignX?: 'left' | 'center' | 'right';
readonly effect?: 'none' | 'hover';
readonly padding?: 'none' | 'small' | 'medium' | 'large';
readonly onClick?: () => void;
readonly children?: ReactNode;
};

export function ClickArea({ padding = 'none', onClick, children }: Props)
export function ClickArea({ alignX = 'left', effect = 'none', padding = 'none', onClick, children }: Props)
{
const className = 'clickarea'
+ ' padding-' + padding;
+ ' padding-' + padding
+ ' align-x-' + alignX
+ ' effect-' + effect;

return <div className={className} onClick={onClick}>
{children}
Expand Down
Loading