Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d7c882324 | |||
| c508d6ae31 | |||
| 02ae2b143e |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@unkn0wndo3s/nova-design-system",
|
"name": "@unkn0wndo3s/nova-design-system",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.02.0",
|
"version": "0.03.0",
|
||||||
"main": "./src/index.ts",
|
"main": "./src/index.ts",
|
||||||
"description": "Nova Design System — Astro component library",
|
"description": "Nova Design System — Astro component library",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -34,4 +34,7 @@
|
|||||||
background-color: var(--nds-disabled);
|
background-color: var(--nds-disabled);
|
||||||
color: var(--nds-neutral);
|
color: var(--nds-neutral);
|
||||||
}
|
}
|
||||||
|
&--icon {
|
||||||
|
padding: var(--nds-spacing-2xs) var(--nds-spacing-xs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
type: 'primary' | 'secondary';
|
type: 'primary' | 'secondary';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
icon?: boolean;
|
||||||
}
|
}
|
||||||
const { type, disabled = false } = Astro.props;
|
const { type, disabled = false, icon = false } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class={`button button__${type} ${disabled ? 'button--disabled' : ''}`}>
|
<div class={`button button__${type} ${disabled ? 'button--disabled' : ''} ${icon ? 'button--icon' : ''}`}>
|
||||||
<slot/>
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ export { default as ListItem } from './ListItem/listItem.astro';
|
|||||||
export { default as ListItemTitle } from './ListItem/listItemTitle.astro';
|
export { default as ListItemTitle } from './ListItem/listItemTitle.astro';
|
||||||
export { default as ListItemSubtitle } from './ListItem/listItemSubtitle.astro';
|
export { default as ListItemSubtitle } from './ListItem/listItemSubtitle.astro';
|
||||||
export { default as LoadingBar } from './LoadingBar/loadingBar.astro';
|
export { default as LoadingBar } from './LoadingBar/loadingBar.astro';
|
||||||
|
export { default as NumericStepper } from './numericStepper/numericStepper.astro';
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@use '../../styles/tokens/typography' as *;
|
||||||
|
|
||||||
|
.numeric-stepper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--nds-spacing-xs);
|
||||||
|
width: fit-content;
|
||||||
|
& p {
|
||||||
|
@include text-xl;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
import Button from '../Button/button.astro';
|
||||||
|
import { Arrow2Icon } from '../Icons';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
id: string;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
value?: number;
|
||||||
|
step?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, min, max, value = 0, step = 1 } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="numeric-stepper"
|
||||||
|
id={id}
|
||||||
|
data-numeric-stepper
|
||||||
|
data-min={min}
|
||||||
|
data-max={max}
|
||||||
|
data-value={value}
|
||||||
|
data-step={step}
|
||||||
|
>
|
||||||
|
<span class="numeric-stepper__control" data-action="increment">
|
||||||
|
<Button type="secondary" icon={true}>
|
||||||
|
<Arrow2Icon size={24} orientation="up" />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<p class="numeric-stepper__value">{value}</p>
|
||||||
|
|
||||||
|
<span class="numeric-stepper__control" data-action="decrement">
|
||||||
|
<Button type="secondary" icon={true}>
|
||||||
|
<Arrow2Icon size={24} orientation="down" />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
type StepperAction = 'increment' | 'decrement';
|
||||||
|
|
||||||
|
interface StepperChangeDetail {
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StepperSetDetail {
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NumericStepperElement extends HTMLElement {
|
||||||
|
setValue: (value: number) => void;
|
||||||
|
getValue: () => number;
|
||||||
|
getStep: () => number;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementEventMap {
|
||||||
|
'stepper:change': CustomEvent<StepperChangeDetail>;
|
||||||
|
'stepper:set': CustomEvent<StepperSetDetail>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll<NumericStepperElement>('[data-numeric-stepper]').forEach((stepper) => {
|
||||||
|
const { min: minAttr, max: maxAttr, step: stepAttr, value: valueAttr } = stepper.dataset;
|
||||||
|
|
||||||
|
const min = minAttr !== undefined ? Number(minAttr) : -Infinity;
|
||||||
|
const max = maxAttr !== undefined ? Number(maxAttr) : Infinity;
|
||||||
|
const step = stepAttr !== undefined ? Number(stepAttr) : 1;
|
||||||
|
|
||||||
|
let current = valueAttr !== undefined ? Number(valueAttr) : 0;
|
||||||
|
|
||||||
|
const display = stepper.querySelector<HTMLParagraphElement>('.numeric-stepper__value');
|
||||||
|
|
||||||
|
function render(): void {
|
||||||
|
if (display) display.textContent = String(current);
|
||||||
|
stepper.dataset.value = String(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setValue(newValue: number): void {
|
||||||
|
const clamped = Math.min(max, Math.max(min, newValue));
|
||||||
|
if (clamped === current) return;
|
||||||
|
|
||||||
|
current = clamped;
|
||||||
|
render();
|
||||||
|
|
||||||
|
stepper.dispatchEvent(
|
||||||
|
new CustomEvent<StepperChangeDetail>('stepper:change', {
|
||||||
|
detail: { value: current },
|
||||||
|
bubbles: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stepper.addEventListener('click', (event: MouseEvent) => {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
const control = target.closest<HTMLElement>('[data-action]');
|
||||||
|
if (!control || !stepper.contains(control)) return;
|
||||||
|
|
||||||
|
const action = control.dataset.action as StepperAction | undefined;
|
||||||
|
if (action === 'increment') setValue(current + step);
|
||||||
|
else if (action === 'decrement') setValue(current - step);
|
||||||
|
});
|
||||||
|
|
||||||
|
stepper.addEventListener('stepper:set', (event) => {
|
||||||
|
setValue(event.detail.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
stepper.setValue = (value: number): void => setValue(value);
|
||||||
|
stepper.getValue = (): number => current;
|
||||||
|
stepper.getStep = (): number => step;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use './numericStepper';
|
||||||
|
</style>
|
||||||
@@ -11,6 +11,7 @@ import ListItem from '../components/ListItem/listItem.astro';
|
|||||||
import ListItemTitle from '../components/ListItem/listItemTitle.astro';
|
import ListItemTitle from '../components/ListItem/listItemTitle.astro';
|
||||||
import ListItemSubtitle from '../components/ListItem/listItemSubtitle.astro';
|
import ListItemSubtitle from '../components/ListItem/listItemSubtitle.astro';
|
||||||
import LoadingBar from '../components/LoadingBar/loadingBar.astro';
|
import LoadingBar from '../components/LoadingBar/loadingBar.astro';
|
||||||
|
import NumericStepper from '../components/numericStepper/numericStepper.astro';
|
||||||
import {
|
import {
|
||||||
Arrow2Icon, BinIcon, BurgerIcon, CalendarIcon, CheckIcon, CloseIcon,
|
Arrow2Icon, BinIcon, BurgerIcon, CalendarIcon, CheckIcon, CloseIcon,
|
||||||
CodeIcon, CubeIcon, DownloadIcon, FilterIcon, HelpIcon, HomeIcon,
|
CodeIcon, CubeIcon, DownloadIcon, FilterIcon, HelpIcon, HomeIcon,
|
||||||
@@ -25,6 +26,20 @@ const initialChecked = true;
|
|||||||
<Layout>
|
<Layout>
|
||||||
<main>
|
<main>
|
||||||
<h1>Nova Design System</h1>
|
<h1>Nova Design System</h1>
|
||||||
|
<section>
|
||||||
|
<h2>Numeric Stepper</h2>
|
||||||
|
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||||
|
<NumericStepper min={-15} max={10} value={5} step={5} id="my-numeric-stepper"/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>Loading Bar</h2>
|
||||||
|
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||||
|
<LoadingBar type="known" percentage={75} width="500px" />
|
||||||
|
<LoadingBar type="unknown" width="500px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Loading Bar</h2>
|
<h2>Loading Bar</h2>
|
||||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||||
|
|||||||
Reference in New Issue
Block a user