ecmc-design-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/dist/atoms/button/Button.svelte +61 -0
- package/dist/atoms/button/Button.svelte.d.ts +4 -0
- package/dist/atoms/button/constants.d.ts +7 -0
- package/dist/atoms/button/constants.js +8 -0
- package/dist/atoms/button/types.d.ts +35 -0
- package/dist/atoms/container/Container.svelte +106 -0
- package/dist/atoms/container/Container.svelte.d.ts +4 -0
- package/dist/atoms/container/constants.d.ts +16 -0
- package/dist/atoms/container/constants.js +19 -0
- package/dist/atoms/container/sub/box.container.svelte +33 -0
- package/dist/atoms/container/sub/box.container.svelte.d.ts +4 -0
- package/dist/atoms/container/sub/centered.container.svelte +28 -0
- package/dist/atoms/container/sub/centered.container.svelte.d.ts +4 -0
- package/dist/atoms/container/sub/gridbox.container.svelte +32 -0
- package/dist/atoms/container/sub/gridbox.container.svelte.d.ts +4 -0
- package/dist/atoms/container/sub/hbox.container.svelte +10 -0
- package/dist/atoms/container/sub/hbox.container.svelte.d.ts +4 -0
- package/dist/atoms/container/sub/vbox.container.svelte +10 -0
- package/dist/atoms/container/sub/vbox.container.svelte.d.ts +4 -0
- package/dist/atoms/container/types.d.ts +252 -0
- package/dist/atoms/input/Input.svelte +75 -0
- package/dist/atoms/input/Input.svelte.d.ts +4 -0
- package/dist/atoms/input/constants.d.ts +2 -0
- package/dist/atoms/input/constants.js +6 -0
- package/dist/atoms/input/types.d.ts +92 -0
- package/dist/atoms/text/Text.svelte +68 -0
- package/dist/atoms/text/Text.svelte.d.ts +4 -0
- package/dist/atoms/text/constants.d.ts +23 -0
- package/dist/atoms/text/constants.js +27 -0
- package/dist/atoms/text/types.d.ts +62 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +11 -0
- package/dist/utils/cn.d.ts +6 -0
- package/dist/utils/cn.js +8 -0
- package/dist/utils/ecmc.css +126 -0
- package/package.json +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ecmc-design-core
|
|
2
|
+
|
|
3
|
+
Atoms and molecules for the ECMC design system built with Svelte 5.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This is an opinionated component library providing foundational UI components (atoms) and compound components (molecules) for building consistent user interfaces. The library is built with Svelte 5 and includes TypeScript support and Storybook integration.
|
|
8
|
+
|
|
9
|
+
## Development
|
|
10
|
+
|
|
11
|
+
Start the development server:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
bun dev
|
|
15
|
+
# or
|
|
16
|
+
npm run dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
View components in Storybook:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
bun storybook
|
|
23
|
+
# or
|
|
24
|
+
npm run storybook
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The Storybook interface will be available at `http://localhost:6006`.
|
|
28
|
+
|
|
29
|
+
## Building
|
|
30
|
+
|
|
31
|
+
To build the preview app:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
bun build
|
|
35
|
+
# or
|
|
36
|
+
npm run build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Scripts
|
|
40
|
+
|
|
41
|
+
- `dev` - Start development server
|
|
42
|
+
- `build` - Build the library for production
|
|
43
|
+
- `storybook` - Launch Storybook development server
|
|
44
|
+
- `build-storybook` - Build Storybook for deployment
|
|
45
|
+
- `lint` - Run linting checks
|
|
46
|
+
- `format` - Format code with Prettier
|
|
47
|
+
- `check` - Run Svelte type checking
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
|
52
|
+
|
|
53
|
+
## Author
|
|
54
|
+
|
|
55
|
+
Arad Fadaei - [fadaei.dev](https://www.fadaei.dev)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Centered from '../container/sub/centered.container.svelte';
|
|
3
|
+
import Text from '../text/Text.svelte';
|
|
4
|
+
import { cn } from '../../utils/cn.js';
|
|
5
|
+
import { buttonVariantClasses, defaultProps } from './constants.js';
|
|
6
|
+
import type { ButtonProps } from './types.js';
|
|
7
|
+
|
|
8
|
+
let { children, onclick, variant = defaultProps.variant! }: ButtonProps = $props();
|
|
9
|
+
|
|
10
|
+
let computedClasses = $derived(cn(buttonVariantClasses[variant]));
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<button class={computedClasses} disabled={variant === 'disabled'} {onclick}>
|
|
14
|
+
<Centered padding="xsm">
|
|
15
|
+
<Text>
|
|
16
|
+
{@render children?.()}
|
|
17
|
+
</Text>
|
|
18
|
+
</Centered>
|
|
19
|
+
</button>
|
|
20
|
+
|
|
21
|
+
<style>
|
|
22
|
+
button {
|
|
23
|
+
border: none;
|
|
24
|
+
border-radius: var(--rounding-size-1);
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.button--primary {
|
|
29
|
+
background-color: var(--primary-600);
|
|
30
|
+
color: var(--neutral-100);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.button--primary:hover {
|
|
34
|
+
background-color: var(--primary-700);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.button--primary:active {
|
|
38
|
+
background-color: var(--primary-900);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.button--secondary {
|
|
42
|
+
background-color: var(--neutral-400);
|
|
43
|
+
color: var(--neutral-100);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.button--secondary:hover {
|
|
47
|
+
background-color: var(--neutral-500);
|
|
48
|
+
color: var(--neutral-100);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.button--secondary:active {
|
|
52
|
+
background-color: var(--neutral-600);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.button--disabled {
|
|
56
|
+
background-color: var(--neutral-300);
|
|
57
|
+
color: var(--neutral-600);
|
|
58
|
+
cursor: not-allowed;
|
|
59
|
+
opacity: 0.6;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ButtonProps } from './types.js';
|
|
2
|
+
export declare const defaultProps: Partial<ButtonProps>;
|
|
3
|
+
export declare const buttonVariantClasses: {
|
|
4
|
+
readonly primary: "button--primary";
|
|
5
|
+
readonly secondary: "button--secondary";
|
|
6
|
+
readonly disabled: "button--disabled";
|
|
7
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the Button component
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <Button variant="primary" onclick={handleClick}>
|
|
9
|
+
* Click Me
|
|
10
|
+
* </Button>
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export interface ButtonProps {
|
|
14
|
+
/**
|
|
15
|
+
* The content to be rendered inside the button
|
|
16
|
+
*/
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Visual variant that determines the button's appearance
|
|
21
|
+
* - `primary`: Main action button with prominent styling
|
|
22
|
+
* - `secondary`: Alternative action with less emphasis
|
|
23
|
+
* - `disabled`: Non-interactive state
|
|
24
|
+
*
|
|
25
|
+
* @default 'primary'
|
|
26
|
+
*/
|
|
27
|
+
variant?: 'primary' | 'secondary' | 'disabled';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Click handler for button interactions
|
|
31
|
+
*
|
|
32
|
+
* @param event - The native mouse click event
|
|
33
|
+
*/
|
|
34
|
+
onclick?: (event: MouseEvent) => void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils/cn.js';
|
|
3
|
+
import type { ContainerProps } from './types.js';
|
|
4
|
+
import { defaultProps, paddingClasses } from './constants.js';
|
|
5
|
+
|
|
6
|
+
// Props with defaults
|
|
7
|
+
let {
|
|
8
|
+
children,
|
|
9
|
+
padding = defaultProps.padding!,
|
|
10
|
+
fill = defaultProps.fill!,
|
|
11
|
+
metrics = $bindable(),
|
|
12
|
+
bg = defaultProps.bg!,
|
|
13
|
+
...restProps
|
|
14
|
+
}: ContainerProps = $props();
|
|
15
|
+
|
|
16
|
+
let computedClasses = $derived(
|
|
17
|
+
cn(paddingClasses[padding], fill ? 'container--fill' : '', bg ? 'container--bg' : '')
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Container element reference
|
|
21
|
+
let containerElement: HTMLDivElement;
|
|
22
|
+
|
|
23
|
+
// Update metrics when element changes or on resize/scroll
|
|
24
|
+
function updateMetrics() {
|
|
25
|
+
if (!containerElement) return;
|
|
26
|
+
|
|
27
|
+
const rect = containerElement.getBoundingClientRect();
|
|
28
|
+
|
|
29
|
+
// Create a new metrics object to trigger reactivity
|
|
30
|
+
metrics = {
|
|
31
|
+
width: rect.width,
|
|
32
|
+
height: rect.height,
|
|
33
|
+
top: rect.top,
|
|
34
|
+
left: rect.left,
|
|
35
|
+
offsetTop: containerElement.offsetTop,
|
|
36
|
+
offsetLeft: containerElement.offsetLeft,
|
|
37
|
+
scrollWidth: containerElement.scrollWidth,
|
|
38
|
+
scrollHeight: containerElement.scrollHeight,
|
|
39
|
+
scrollTop: containerElement.scrollTop,
|
|
40
|
+
scrollLeft: containerElement.scrollLeft,
|
|
41
|
+
clientWidth: containerElement.clientWidth,
|
|
42
|
+
clientHeight: containerElement.clientHeight
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Initialize metrics when element is bound
|
|
47
|
+
$effect(() => {
|
|
48
|
+
if (containerElement) {
|
|
49
|
+
updateMetrics();
|
|
50
|
+
|
|
51
|
+
// Create ResizeObserver to track size changes
|
|
52
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
53
|
+
updateMetrics();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
resizeObserver.observe(containerElement);
|
|
57
|
+
containerElement.addEventListener('scroll', updateMetrics);
|
|
58
|
+
|
|
59
|
+
// Cleanup function
|
|
60
|
+
return () => {
|
|
61
|
+
resizeObserver.disconnect();
|
|
62
|
+
containerElement.removeEventListener('scroll', updateMetrics);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return () => {}; // Explicit return for when containerElement is not available
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<div bind:this={containerElement} class={computedClasses} {...restProps}>
|
|
70
|
+
{@render children?.()}
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<style>
|
|
74
|
+
.container--padding-none {
|
|
75
|
+
padding: var(--spacing-none);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.container--padding-xsm {
|
|
79
|
+
padding: var(--spacing-xsm);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.container--padding-sm {
|
|
83
|
+
padding: var(--spacing-sm);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.container--padding-md {
|
|
87
|
+
padding: var(--spacing-md);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.container--padding-lg {
|
|
91
|
+
padding: var(--spacing-lg);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.container--padding-xl {
|
|
95
|
+
padding: var(--spacing-xl);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.container--fill {
|
|
99
|
+
height: 100%;
|
|
100
|
+
width: 100%;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.container--bg {
|
|
104
|
+
background-color: light-dark(var(--neutral-200), var(--neutral-800));
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ContainerProps } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default props for the container atom
|
|
4
|
+
*/
|
|
5
|
+
export declare const defaultProps: Partial<ContainerProps>;
|
|
6
|
+
/**
|
|
7
|
+
* CSS class mappings for padding variants
|
|
8
|
+
*/
|
|
9
|
+
export declare const paddingClasses: {
|
|
10
|
+
readonly none: "container--padding-none";
|
|
11
|
+
readonly xsm: "container--padding-xsm";
|
|
12
|
+
readonly sm: "container--padding-sm";
|
|
13
|
+
readonly md: "container--padding-md";
|
|
14
|
+
readonly lg: "container--padding-lg";
|
|
15
|
+
readonly xl: "container--padding-xl";
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default props for the container atom
|
|
3
|
+
*/
|
|
4
|
+
export const defaultProps = {
|
|
5
|
+
padding: 'md',
|
|
6
|
+
fill: false,
|
|
7
|
+
bg: false
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* CSS class mappings for padding variants
|
|
11
|
+
*/
|
|
12
|
+
export const paddingClasses = {
|
|
13
|
+
none: 'container--padding-none',
|
|
14
|
+
xsm: 'container--padding-xsm',
|
|
15
|
+
sm: 'container--padding-sm',
|
|
16
|
+
md: 'container--padding-md',
|
|
17
|
+
lg: 'container--padding-lg',
|
|
18
|
+
xl: 'container--padding-xl'
|
|
19
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Container from '../Container.svelte';
|
|
3
|
+
import type { InternalBoxProps } from '../types.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
variant,
|
|
8
|
+
gap = 'md',
|
|
9
|
+
align = 'stretch',
|
|
10
|
+
justify = 'flex-start',
|
|
11
|
+
wrap = false,
|
|
12
|
+
metrics = $bindable(),
|
|
13
|
+
...containerProps
|
|
14
|
+
}: InternalBoxProps = $props();
|
|
15
|
+
|
|
16
|
+
let flexWrap = $derived(wrap ? 'wrap' : 'nowrap');
|
|
17
|
+
let flexDirection = $derived(variant === 'vertical' ? 'column' : 'row');
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<Container bind:metrics {...containerProps}>
|
|
21
|
+
<div
|
|
22
|
+
class="box"
|
|
23
|
+
style="gap: var(--spacing-{gap}); align-items: {align}; justify-content: {justify}; flex-wrap: {flexWrap}; flex-direction: {flexDirection};"
|
|
24
|
+
>
|
|
25
|
+
{@render children?.()}
|
|
26
|
+
</div>
|
|
27
|
+
</Container>
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.box {
|
|
31
|
+
display: flex;
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Container from '../Container.svelte';
|
|
3
|
+
import type { CenteredProps } from '../types.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
horizontal = true,
|
|
8
|
+
vertical = true,
|
|
9
|
+
metrics = $bindable(),
|
|
10
|
+
...containerProps
|
|
11
|
+
}: CenteredProps = $props();
|
|
12
|
+
|
|
13
|
+
let justifyContent = $derived(horizontal ? 'center' : 'flex-start');
|
|
14
|
+
let alignItems = $derived(vertical ? 'center' : 'stretch');
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<Container bind:metrics {...containerProps}>
|
|
18
|
+
<div class="centered" style="justify-content: {justifyContent}; align-items: {alignItems};">
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
21
|
+
</Container>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
.centered {
|
|
25
|
+
display: flex;
|
|
26
|
+
height: 100%;
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Container from '../Container.svelte';
|
|
3
|
+
import type { GridBoxProps } from '../types.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
columns = 2,
|
|
8
|
+
gap = 'md',
|
|
9
|
+
autoFlow = 'row',
|
|
10
|
+
metrics = $bindable(),
|
|
11
|
+
...containerProps
|
|
12
|
+
}: GridBoxProps = $props();
|
|
13
|
+
|
|
14
|
+
let gridTemplateColumns = $derived(
|
|
15
|
+
typeof columns === 'number' ? `repeat(${columns}, 1fr)` : columns
|
|
16
|
+
);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<Container bind:metrics {...containerProps}>
|
|
20
|
+
<div
|
|
21
|
+
class="gridbox"
|
|
22
|
+
style="grid-template-columns: {gridTemplateColumns}; gap: var(--spacing-{gap}); grid-auto-flow: {autoFlow};"
|
|
23
|
+
>
|
|
24
|
+
{@render children?.()}
|
|
25
|
+
</div>
|
|
26
|
+
</Container>
|
|
27
|
+
|
|
28
|
+
<style>
|
|
29
|
+
.gridbox {
|
|
30
|
+
display: grid;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Box from './box.container.svelte';
|
|
3
|
+
import type { BoxProps } from '../types.js';
|
|
4
|
+
|
|
5
|
+
let { metrics = $bindable(), ...props }: BoxProps = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Box variant="horizontal" bind:metrics {...props}>
|
|
9
|
+
{@render props.children?.()}
|
|
10
|
+
</Box>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Box from './box.container.svelte';
|
|
3
|
+
import type { BoxProps } from '../types.js';
|
|
4
|
+
|
|
5
|
+
let { metrics = $bindable(), ...props }: BoxProps = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<Box variant="vertical" bind:metrics {...props}>
|
|
9
|
+
{@render props.children?.()}
|
|
10
|
+
</Box>
|