bref-ui 0.2.2 → 0.2.4

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 CHANGED
@@ -55,7 +55,8 @@ Bref handles:
55
55
  - [x] Icon
56
56
  - [x] Button
57
57
  - [x] Icon Button
58
- - [x] Loading
58
+ - [x] Progress / Loading
59
+ - [x] Progress Bar
59
60
  - [x] Circular
60
61
  - [x] Pulsing Dots
61
62
  - [x] Morphing Shapes
@@ -64,12 +65,11 @@ Bref handles:
64
65
  - [-] Inputs
65
66
  - [x] Text Input (single line)
66
67
  - [x] Area Text Input
68
+ - [x] Slider (range)
67
69
  - [ ] File Input
68
70
  - [ ] Image Input
69
71
  - [ ] Avatar
70
- - [ ] Progress
71
72
  - [ ] Select
72
- - [ ] Slider
73
73
  - [ ] Skeleton
74
74
  - [ ] Checkbox
75
75
  - [ ] Badge
@@ -5,3 +5,5 @@ export * from './button/index.ts';
5
5
  export * from './loading/index.ts';
6
6
  export * from './tree-view/index.ts';
7
7
  export * from './text-input/index.ts';
8
+ export * from './slider/index.ts';
9
+ export * from './progress-bar/index.ts';
@@ -5,3 +5,5 @@ export * from "./button/index.js";
5
5
  export * from "./loading/index.js";
6
6
  export * from "./tree-view/index.js";
7
7
  export * from "./text-input/index.js";
8
+ export * from "./slider/index.js";
9
+ export * from "./progress-bar/index.js";
@@ -0,0 +1 @@
1
+ export { default as ProgressBar } from './progress-bar.svelte';
@@ -0,0 +1 @@
1
+ export { default as ProgressBar } from './progress-bar.svelte';
@@ -0,0 +1,172 @@
1
+ <script lang="ts">
2
+ import type { ProgressBarProps } from './types.js';
3
+
4
+ const {
5
+ value,
6
+ size = 'medium',
7
+ color = 'primary',
8
+ wide = false,
9
+ animateValue = false,
10
+ onValueClick
11
+ }: ProgressBarProps = $props();
12
+
13
+ const isIndeterminate = $derived(value === undefined);
14
+ const progressWidth = $derived(
15
+ value === undefined ? undefined : `${Math.min(100, Math.max(0, value))}%`
16
+ );
17
+ const isClickable = $derived(onValueClick !== undefined);
18
+
19
+ const handleClick = (e: MouseEvent) => {
20
+ if (!onValueClick) return;
21
+ const element = e.currentTarget as HTMLElement;
22
+ const rect = element.getBoundingClientRect();
23
+ const clickX = e.clientX - rect.left;
24
+ const percentage = Math.round((clickX / rect.width) * 100);
25
+ onValueClick(Math.min(100, Math.max(0, percentage)));
26
+ };
27
+ </script>
28
+
29
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
30
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
31
+ <!-- aria-valuenow={isIndeterminate ? undefined : clampedValue} -->
32
+ <div
33
+ class={`${size} ${color}`}
34
+ class:wide
35
+ class:clickable={isClickable}
36
+ class:indeterminate={isIndeterminate}
37
+ class:animate={animateValue}
38
+ role="progressbar"
39
+ aria-valuemin={0}
40
+ aria-valuemax={100}
41
+ onclick={isClickable ? handleClick : undefined}
42
+ >
43
+ <span class:indeterminate={isIndeterminate} style:width={progressWidth}></span>
44
+ </div>
45
+
46
+ <style>
47
+ div {
48
+ --internal-progress-height: 0.5rem;
49
+ --internal-progress-radius: calc(var(--internal-progress-height) * 0.5);
50
+
51
+ position: relative;
52
+ display: flex;
53
+ width: 12rem;
54
+ height: var(--internal-progress-height);
55
+ background-color: var(--internal-current-color-soft);
56
+ border-radius: var(--internal-progress-radius);
57
+ overflow: hidden;
58
+ }
59
+
60
+ .wide {
61
+ width: 100%;
62
+ }
63
+
64
+ .clickable {
65
+ cursor: pointer;
66
+ }
67
+
68
+ span {
69
+ height: 100%;
70
+ display: flex;
71
+ background-color: var(--internal-current-color);
72
+ border-radius: var(--internal-progress-radius);
73
+ }
74
+
75
+ .animate span:not(.indeterminate) {
76
+ transition: width 0.3s ease-out;
77
+ }
78
+
79
+ span.indeterminate {
80
+ animation: indeterminate-fill 1.5s cubic-bezier(0.4, 0, 0.2, 1) infinite;
81
+ }
82
+
83
+ div.indeterminate {
84
+ justify-content: flex-start;
85
+ animation: indeterminate-container 1.5s cubic-bezier(0.4, 0, 0.2, 1) infinite;
86
+ }
87
+
88
+ @keyframes indeterminate-fill {
89
+ 0%,
90
+ 100% {
91
+ width: 25%;
92
+ opacity: 0.5;
93
+ }
94
+
95
+ 50% {
96
+ width: 100%;
97
+ opacity: 0.25;
98
+ }
99
+ }
100
+
101
+ @keyframes indeterminate-container {
102
+ 0%,
103
+ 100% {
104
+ padding-left: 0;
105
+ }
106
+ 50% {
107
+ padding-left: 75%;
108
+ }
109
+ }
110
+
111
+ /* Sizes */
112
+ .x-small {
113
+ --internal-progress-height: 0.25rem;
114
+ }
115
+
116
+ .small {
117
+ --internal-progress-height: 0.375rem;
118
+ }
119
+
120
+ .medium {
121
+ --internal-progress-height: 0.5rem;
122
+ }
123
+
124
+ .large {
125
+ --internal-progress-height: 0.625rem;
126
+ }
127
+
128
+ .x-large {
129
+ --internal-progress-height: 0.75rem;
130
+ }
131
+
132
+ /* Color mappings */
133
+ .primary {
134
+ --internal-current-color: var(--color-primary);
135
+ --internal-current-color-soft: var(--color-primary-soft);
136
+ }
137
+
138
+ .secondary {
139
+ --internal-current-color: var(--color-secondary);
140
+ --internal-current-color-soft: var(--color-secondary-soft);
141
+ }
142
+
143
+ .success {
144
+ --internal-current-color: var(--color-success);
145
+ --internal-current-color-soft: var(--color-success-soft);
146
+ }
147
+
148
+ .warning {
149
+ --internal-current-color: var(--color-warning);
150
+ --internal-current-color-soft: var(--color-warning-soft);
151
+ }
152
+
153
+ .danger {
154
+ --internal-current-color: var(--color-danger);
155
+ --internal-current-color-soft: var(--color-danger-soft);
156
+ }
157
+
158
+ .info {
159
+ --internal-current-color: var(--color-info);
160
+ --internal-current-color-soft: var(--color-info-soft);
161
+ }
162
+
163
+ .foreground {
164
+ --internal-current-color: var(--color-foreground);
165
+ --internal-current-color-soft: var(--color-background-saturated);
166
+ }
167
+
168
+ .background {
169
+ --internal-current-color: var(--color-background);
170
+ --internal-current-color-soft: var(--color-foreground-saturated);
171
+ }
172
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ProgressBarProps } from './types.ts';
2
+ declare const ProgressBar: import("svelte").Component<ProgressBarProps, {}, "">;
3
+ type ProgressBar = ReturnType<typeof ProgressBar>;
4
+ export default ProgressBar;
@@ -0,0 +1,9 @@
1
+ import type { Size, Color } from '../types.ts';
2
+ export interface ProgressBarProps {
3
+ value?: number;
4
+ size?: Size;
5
+ color?: Color;
6
+ wide?: boolean;
7
+ animateValue?: boolean;
8
+ onValueClick?: (clickedValue: number) => void;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export { default as Slider } from './slider.svelte';
@@ -0,0 +1 @@
1
+ export { default as Slider } from './slider.svelte';
@@ -0,0 +1,190 @@
1
+ <script lang="ts">
2
+ import type { SvelteHTMLElements } from 'svelte/elements';
3
+ import type { SliderProps } from './types.js';
4
+
5
+ const {
6
+ value,
7
+ min = 0,
8
+ max = 100,
9
+ step = 1,
10
+ disabled = false,
11
+ wide = false,
12
+ size = 'medium',
13
+ color = 'primary',
14
+ onChange,
15
+ ...rest
16
+ }: SliderProps &
17
+ Omit<
18
+ SvelteHTMLElements['input'],
19
+ 'value' | 'step' | 'min' | 'max' | 'disabled' | 'size'
20
+ > = $props();
21
+
22
+ const ratio = $derived((value - min) / (max - min));
23
+
24
+ const handleInput = (e: Event) => {
25
+ const newValue = Number((e.currentTarget as HTMLInputElement).value);
26
+ onChange(newValue);
27
+ };
28
+ </script>
29
+
30
+ <div class="track {size} {color}" class:wide class:disabled>
31
+ <div
32
+ class="fill"
33
+ style:width="calc({ratio} * (100% - var(--internal-track-height)) + var(--internal-track-height) /
34
+ 2)"
35
+ ></div>
36
+ <input {...rest} type="range" {min} {max} {step} {value} {disabled} oninput={handleInput} />
37
+ </div>
38
+
39
+ <style>
40
+ .track {
41
+ --internal-track-height: 0.5rem;
42
+ --internal-color: var(--color-primary);
43
+ --internal-color-soft: var(--color-primary-soft);
44
+ --internal-thumb-color: var(--color-primary-saturated);
45
+
46
+ position: relative;
47
+ min-width: 7.5rem;
48
+ height: var(--internal-track-height);
49
+ background: var(--internal-color-soft);
50
+ border-radius: calc(var(--internal-track-height) / 2);
51
+ }
52
+
53
+ .track.wide {
54
+ width: 100%;
55
+ }
56
+
57
+ /* Color variants */
58
+ .track.primary {
59
+ --internal-color: var(--color-primary);
60
+ --internal-color-soft: var(--color-primary-soft);
61
+ --internal-thumb-color: var(--color-primary-saturated);
62
+ }
63
+
64
+ .track.foreground {
65
+ --internal-color: var(--color-foreground);
66
+ --internal-color-soft: var(--color-foreground-soft);
67
+ --internal-thumb-color: var(--color-foreground-saturated);
68
+ }
69
+
70
+ /* Size variants */
71
+ .track.x-small {
72
+ --internal-track-height: 0.25rem;
73
+ }
74
+
75
+ .track.small {
76
+ --internal-track-height: 0.375rem;
77
+ }
78
+
79
+ .track.medium {
80
+ --internal-track-height: 0.5rem;
81
+ }
82
+
83
+ .track.large {
84
+ --internal-track-height: 0.625rem;
85
+ }
86
+
87
+ .track.x-large {
88
+ --internal-track-height: 0.75rem;
89
+ }
90
+
91
+ /* Fill bar */
92
+ .fill {
93
+ position: absolute;
94
+ top: 0;
95
+ left: 0;
96
+ height: 100%;
97
+ background: var(--internal-color);
98
+ border-radius: calc(var(--internal-track-height) / 2) 0 0 calc(var(--internal-track-height) / 2);
99
+ pointer-events: none;
100
+ }
101
+
102
+ /* Input */
103
+ input {
104
+ position: absolute;
105
+ top: 0;
106
+ left: 0;
107
+ width: 100%;
108
+ height: 100%;
109
+ -webkit-appearance: none;
110
+ appearance: none;
111
+ background: transparent;
112
+ outline: none;
113
+ cursor: pointer;
114
+ }
115
+
116
+ input:focus-visible {
117
+ outline: 0.1rem solid var(--internal-color);
118
+ outline-offset: 0.2rem;
119
+ }
120
+
121
+ /* Webkit thumb */
122
+ input::-webkit-slider-thumb {
123
+ -webkit-appearance: none;
124
+ appearance: none;
125
+ width: var(--internal-track-height);
126
+ height: var(--internal-track-height);
127
+ border-radius: 50%;
128
+ background: var(--internal-thumb-color);
129
+ cursor: pointer;
130
+ border: none;
131
+ box-shadow: 0 0.03rem 0.125rem rgba(0, 0, 0, 0.08);
132
+ transition:
133
+ transform 0.2s ease,
134
+ box-shadow 0.2s ease;
135
+ }
136
+
137
+ input::-webkit-slider-thumb:hover {
138
+ transform: scale(1.2);
139
+ box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.1);
140
+ }
141
+
142
+ input::-webkit-slider-thumb:active {
143
+ transform: scale(1.1);
144
+ }
145
+
146
+ /* Firefox thumb */
147
+ input::-moz-range-thumb {
148
+ width: var(--internal-track-height);
149
+ height: var(--internal-track-height);
150
+ border-radius: 50%;
151
+ background: var(--internal-thumb-color);
152
+ cursor: pointer;
153
+ border: none;
154
+ box-shadow: 0 0.03rem 0.125rem rgba(0, 0, 0, 0.08);
155
+ transition:
156
+ transform 0.2s ease,
157
+ box-shadow 0.2s ease;
158
+ }
159
+
160
+ input::-moz-range-thumb:hover {
161
+ transform: scale(1.2);
162
+ box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.1);
163
+ }
164
+
165
+ input::-moz-range-thumb:active {
166
+ transform: scale(1.1);
167
+ }
168
+
169
+ /* Firefox track */
170
+ input::-moz-range-track {
171
+ background: transparent;
172
+ }
173
+
174
+ /* Disabled state */
175
+ .track.disabled {
176
+ opacity: 0.4;
177
+ }
178
+
179
+ .track.disabled input {
180
+ cursor: not-allowed;
181
+ }
182
+
183
+ .track.disabled input::-webkit-slider-thumb {
184
+ cursor: not-allowed;
185
+ }
186
+
187
+ .track.disabled input::-moz-range-thumb {
188
+ cursor: not-allowed;
189
+ }
190
+ </style>
@@ -0,0 +1,6 @@
1
+ import type { SvelteHTMLElements } from 'svelte/elements';
2
+ import type { SliderProps } from './types.ts';
3
+ type $$ComponentProps = SliderProps & Omit<SvelteHTMLElements['input'], 'value' | 'step' | 'min' | 'max' | 'disabled' | 'size'>;
4
+ declare const Slider: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type Slider = ReturnType<typeof Slider>;
6
+ export default Slider;
@@ -0,0 +1,12 @@
1
+ import type { Size, Color } from '../types.ts';
2
+ export interface SliderProps {
3
+ value: number;
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ disabled?: boolean;
8
+ wide?: boolean;
9
+ size?: Size;
10
+ color?: Extract<Color, 'primary' | 'foreground'>;
11
+ onChange: (value: number) => void;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -44,53 +44,65 @@ export const PAGES = [
44
44
  ]
45
45
  },
46
46
  {
47
- title: 'Text Inputs',
48
- description: 'Text input fields with customizable size, color, and validation states',
49
- href: '/text-inputs',
47
+ title: 'Inputs',
48
+ description: 'Input fields for text, numbers, and selections',
49
+ href: '/inputs',
50
50
  icon: 'input',
51
51
  children: [
52
52
  {
53
53
  title: 'Text Input',
54
54
  description: 'Single-line text input fields with labels, placeholders, and icons',
55
- href: '/text-inputs/text-input',
55
+ href: '/inputs/text-input',
56
56
  icon: 'short_text'
57
57
  },
58
58
  {
59
59
  title: 'Area Text Input',
60
60
  description: 'Multi-line text area input fields with labels and placeholders',
61
- href: '/text-inputs/area-text-input',
61
+ href: '/inputs/area-text-input',
62
62
  icon: 'notes'
63
+ },
64
+ {
65
+ title: 'Slider',
66
+ description: 'Range slider for selecting numeric values within a range',
67
+ href: '/inputs/slider',
68
+ icon: 'tune'
63
69
  }
64
70
  ]
65
71
  },
66
72
  {
67
- title: 'Loading',
68
- description: 'Multiple loading indicator styles for different visual contexts',
69
- href: '/loadings',
73
+ title: 'Progress / Loading',
74
+ description: 'Progress indicators and loading animations',
75
+ href: '/progress',
70
76
  icon: 'hourglass_empty',
71
77
  children: [
78
+ {
79
+ title: 'Progress Bar',
80
+ description: 'A progress bar with determinate and indeterminate states',
81
+ href: '/progress/progress-bar',
82
+ icon: 'linear_scale'
83
+ },
72
84
  {
73
85
  title: 'Circular',
74
86
  description: 'A classic spinning circle indicator for loading states',
75
- href: '/loadings/circular',
87
+ href: '/progress/circular',
76
88
  icon: 'autorenew'
77
89
  },
78
90
  {
79
91
  title: 'Pulsing Dots',
80
92
  description: 'Three dots that pulse in sequence to indicate loading',
81
- href: '/loadings/pulsing-dots',
93
+ href: '/progress/pulsing-dots',
82
94
  icon: 'more_horiz'
83
95
  },
84
96
  {
85
97
  title: 'Morphing Shapes',
86
98
  description: 'A shape that transforms through organic forms while rotating',
87
- href: '/loadings/morphing-shapes',
99
+ href: '/progress/morphing-shapes',
88
100
  icon: 'animation'
89
101
  },
90
102
  {
91
103
  title: 'Textual',
92
104
  description: 'Animated text with a typewriter effect that cycles through words',
93
- href: '/loadings/textual',
105
+ href: '/progress/textual',
94
106
  icon: 'text_fields'
95
107
  }
96
108
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bref-ui",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A Svelte UI component library using scoped CSS - minimal and flexible",
5
5
  "license": "MIT",
6
6
  "author": "feuerstein",