@volksverpetzer/ui-web 0.2.0 → 0.3.0
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/dist/ProgressBar.css +82 -0
- package/dist/ProgressBar.d.ts +23 -0
- package/dist/ProgressBar.js +31 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/styles.css +83 -0
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
.vvp-ui-progress {
|
|
2
|
+
width: 100%;
|
|
3
|
+
text-align: center;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.vvp-ui-progress__amounts {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: baseline;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
gap: var(--vvp-spacing-sm, 8px);
|
|
11
|
+
margin-bottom: var(--vvp-spacing-sm, 8px);
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.vvp-ui-progress__total {
|
|
16
|
+
font-weight: 700;
|
|
17
|
+
font-size: var(--vvp-font-size-xxl, 24px);
|
|
18
|
+
line-height: 1.2;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.vvp-ui-progress__goal {
|
|
22
|
+
font-size: var(--vvp-font-size-sm, 14px);
|
|
23
|
+
color: var(--vvp-text-muted, #666);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.vvp-ui-progress__track-row {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-wrap: nowrap;
|
|
29
|
+
width: 100%;
|
|
30
|
+
gap: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.vvp-ui-progress__step {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
gap: var(--vvp-spacing-xs, 4px);
|
|
37
|
+
min-width: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.vvp-ui-progress__track {
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 24px;
|
|
43
|
+
background: var(--vvp-surface-disabled, #ddd);
|
|
44
|
+
border: 1px solid var(--vvp-surface-input, #badde8);
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
position: relative;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.vvp-ui-progress__step:first-child .vvp-ui-progress__track {
|
|
50
|
+
border-top-left-radius: var(--vvp-radius-full, 9999px);
|
|
51
|
+
border-bottom-left-radius: var(--vvp-radius-full, 9999px);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.vvp-ui-progress__step:last-child .vvp-ui-progress__track {
|
|
55
|
+
border-top-right-radius: var(--vvp-radius-full, 9999px);
|
|
56
|
+
border-bottom-right-radius: var(--vvp-radius-full, 9999px);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.vvp-ui-progress__step + .vvp-ui-progress__step .vvp-ui-progress__track {
|
|
60
|
+
border-left: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.vvp-ui-progress__fill {
|
|
64
|
+
height: 100%;
|
|
65
|
+
border-radius: inherit;
|
|
66
|
+
background: linear-gradient(120deg, var(--vvp-accent, #db2685), color-mix(in srgb, var(--vvp-accent, #db2685) 85%, black));
|
|
67
|
+
transition: width 300ms ease-out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.vvp-ui-progress__step-label {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
align-items: flex-end;
|
|
74
|
+
gap: 2px;
|
|
75
|
+
font-size: var(--vvp-font-size-sm, 14px);
|
|
76
|
+
color: var(--vvp-text-muted, #666);
|
|
77
|
+
text-align: right;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.vvp-ui-progress__step-label strong {
|
|
81
|
+
color: var(--vvp-text, #111);
|
|
82
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
import "./ProgressBar.css";
|
|
3
|
+
export interface ProgressBarMilestone {
|
|
4
|
+
value: number;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ProgressBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
total: number;
|
|
9
|
+
goal: number;
|
|
10
|
+
/**
|
|
11
|
+
* Stretch goals to render as separate segments, e.g. for a campaign with
|
|
12
|
+
* multiple funding tiers. Omit (or pass a single entry) for a plain bar
|
|
13
|
+
* with one segment running from 0 to `goal` — the common case.
|
|
14
|
+
*/
|
|
15
|
+
milestones?: ProgressBarMilestone[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Campaign donation progress bar — one segment by default (0 to `goal`), or
|
|
19
|
+
* multiple stacked segments when `milestones` has more than one entry.
|
|
20
|
+
* Styled off the `--vvp-*` custom properties emitted by
|
|
21
|
+
* `@volksverpetzer/design-tokens`, same as the other components here.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ProgressBar({ total, goal, milestones, className, ...rest }: ProgressBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "./ProgressBar.css";
|
|
3
|
+
const formatEuro = (value) => value.toLocaleString("de-DE", { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + " €";
|
|
4
|
+
/**
|
|
5
|
+
* Campaign donation progress bar — one segment by default (0 to `goal`), or
|
|
6
|
+
* multiple stacked segments when `milestones` has more than one entry.
|
|
7
|
+
* Styled off the `--vvp-*` custom properties emitted by
|
|
8
|
+
* `@volksverpetzer/design-tokens`, same as the other components here.
|
|
9
|
+
*/
|
|
10
|
+
export function ProgressBar({ total, goal, milestones, className, ...rest }) {
|
|
11
|
+
const steps = milestones && milestones.length > 1 ? [...milestones].sort((a, b) => a.value - b.value) : [{ value: goal, label: "" }];
|
|
12
|
+
const isSingleStep = steps.length === 1;
|
|
13
|
+
const target = steps[steps.length - 1].value;
|
|
14
|
+
const segments = steps.map((step, index) => {
|
|
15
|
+
const start = index === 0 ? 0 : steps[index - 1].value;
|
|
16
|
+
const range = step.value - start;
|
|
17
|
+
const filled = Math.min(Math.max(total - start, 0), range);
|
|
18
|
+
const percent = range > 0 ? Math.min(100, (filled / range) * 100) : 0;
|
|
19
|
+
const width = target > 0 ? (range / target) * 100 : 0;
|
|
20
|
+
return { ...step, start, range, filled, percent, width };
|
|
21
|
+
});
|
|
22
|
+
const classes = ["vvp-ui-progress", className].filter(Boolean).join(" ");
|
|
23
|
+
return (_jsxs("div", { className: classes, ...rest, children: [isSingleStep ? (_jsxs("div", { className: "vvp-ui-progress__amounts", children: [_jsx("span", { className: "vvp-ui-progress__total", children: formatEuro(total) }), _jsxs("span", { className: "vvp-ui-progress__goal", children: [" von ", formatEuro(goal)] })] })) : null, _jsx("div", { className: "vvp-ui-progress__track-row", role: "group", "aria-label": "Spendenfortschritt", children: segments.map((segment, index) => {
|
|
24
|
+
const valueNow = Math.min(Math.max(total, segment.start), segment.value);
|
|
25
|
+
return (_jsxs("div", { className: "vvp-ui-progress__step", style: { flexBasis: `${segment.width}%`, flexGrow: segment.width, flexShrink: 0 }, children: [_jsx("div", { className: "vvp-ui-progress__track", role: "progressbar", "aria-valuemin": segment.start, "aria-valuemax": segment.value, "aria-valuenow": valueNow, "aria-valuetext": isSingleStep
|
|
26
|
+
? `${formatEuro(total)} von ${formatEuro(goal)}`
|
|
27
|
+
: `${formatEuro(total)} insgesamt gesammelt, ${formatEuro(segment.filled)} von ${formatEuro(segment.range)} für „${segment.label}“`, "aria-label": isSingleStep
|
|
28
|
+
? `${formatEuro(total)} von ${formatEuro(goal)} gesammelt`
|
|
29
|
+
: `${segment.label} (${formatEuro(segment.value)})`, children: _jsx("div", { className: "vvp-ui-progress__fill", style: { width: `${segment.percent}%` } }) }), !isSingleStep ? (_jsxs("div", { className: "vvp-ui-progress__step-label", children: [_jsx("strong", { children: segment.label }), _jsx("span", { children: formatEuro(segment.value) })] })) : null] }, `${segment.value}-${index}`));
|
|
30
|
+
}) })] }));
|
|
31
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { Button, type ButtonProps, type ButtonVariant, type ButtonSize } from "./Button";
|
|
2
2
|
export { Badge, type BadgeProps, type BadgeVariant } from "./Badge";
|
|
3
3
|
export { Card, type CardProps } from "./Card";
|
|
4
|
+
export { ProgressBar, type ProgressBarProps, type ProgressBarMilestone } from "./ProgressBar";
|
package/dist/index.js
CHANGED
package/dist/styles.css
CHANGED
|
@@ -117,3 +117,86 @@
|
|
|
117
117
|
border: 1px solid var(--vvp-surface-input, #badde8);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
.vvp-ui-progress {
|
|
121
|
+
width: 100%;
|
|
122
|
+
text-align: center;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.vvp-ui-progress__amounts {
|
|
126
|
+
display: flex;
|
|
127
|
+
align-items: baseline;
|
|
128
|
+
justify-content: center;
|
|
129
|
+
gap: var(--vvp-spacing-sm, 8px);
|
|
130
|
+
margin-bottom: var(--vvp-spacing-sm, 8px);
|
|
131
|
+
flex-wrap: wrap;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.vvp-ui-progress__total {
|
|
135
|
+
font-weight: 700;
|
|
136
|
+
font-size: var(--vvp-font-size-xxl, 24px);
|
|
137
|
+
line-height: 1.2;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.vvp-ui-progress__goal {
|
|
141
|
+
font-size: var(--vvp-font-size-sm, 14px);
|
|
142
|
+
color: var(--vvp-text-muted, #666);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.vvp-ui-progress__track-row {
|
|
146
|
+
display: flex;
|
|
147
|
+
flex-wrap: nowrap;
|
|
148
|
+
width: 100%;
|
|
149
|
+
gap: 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.vvp-ui-progress__step {
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: column;
|
|
155
|
+
gap: var(--vvp-spacing-xs, 4px);
|
|
156
|
+
min-width: 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.vvp-ui-progress__track {
|
|
160
|
+
width: 100%;
|
|
161
|
+
height: 24px;
|
|
162
|
+
background: var(--vvp-surface-disabled, #ddd);
|
|
163
|
+
border: 1px solid var(--vvp-surface-input, #badde8);
|
|
164
|
+
overflow: hidden;
|
|
165
|
+
position: relative;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.vvp-ui-progress__step:first-child .vvp-ui-progress__track {
|
|
169
|
+
border-top-left-radius: var(--vvp-radius-full, 9999px);
|
|
170
|
+
border-bottom-left-radius: var(--vvp-radius-full, 9999px);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.vvp-ui-progress__step:last-child .vvp-ui-progress__track {
|
|
174
|
+
border-top-right-radius: var(--vvp-radius-full, 9999px);
|
|
175
|
+
border-bottom-right-radius: var(--vvp-radius-full, 9999px);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.vvp-ui-progress__step + .vvp-ui-progress__step .vvp-ui-progress__track {
|
|
179
|
+
border-left: 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.vvp-ui-progress__fill {
|
|
183
|
+
height: 100%;
|
|
184
|
+
border-radius: inherit;
|
|
185
|
+
background: linear-gradient(120deg, var(--vvp-accent, #db2685), color-mix(in srgb, var(--vvp-accent, #db2685) 85%, black));
|
|
186
|
+
transition: width 300ms ease-out;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.vvp-ui-progress__step-label {
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
align-items: flex-end;
|
|
193
|
+
gap: 2px;
|
|
194
|
+
font-size: var(--vvp-font-size-sm, 14px);
|
|
195
|
+
color: var(--vvp-text-muted, #666);
|
|
196
|
+
text-align: right;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.vvp-ui-progress__step-label strong {
|
|
200
|
+
color: var(--vvp-text, #111);
|
|
201
|
+
}
|
|
202
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volksverpetzer/ui-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared brand-visible React components (Button, Badge, Card) for vvp_link_shortener, vvp_divi5_extensions, and crowdfunding. Plain, hand-scoped CSS (vvp-ui-* class prefix) consuming the --vvp-* custom properties emitted by @volksverpetzer/design-tokens — no Tailwind classes, so it drops into a Tailwind app and Divi's WordPress-embedded CSS alike.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|