@epa-datos/ui 0.1.0 → 0.1.2
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
|
@@ -93,6 +93,14 @@ pnpm build:lib # builds the publishable package into dist/
|
|
|
93
93
|
|
|
94
94
|
Component and design-token changes are made here, in `epa-ui`, not in projects that depend on the package — see `AGENTS.md` for the full editing conventions.
|
|
95
95
|
|
|
96
|
+
### Releasing a new version
|
|
97
|
+
|
|
98
|
+
1. Bump `version` in `package.json` (semver — this is the only manual step).
|
|
99
|
+
2. Commit that change and cut a [GitHub release](https://github.com/epa-datos/epa-ui/releases/new) (tag `vX.Y.Z` matching the version).
|
|
100
|
+
3. Publishing to npm happens automatically from there — the `Publish package` workflow (`.github/workflows/publish.yml`) builds the package and runs `npm publish` on release, and it does not use a stored `NPM_TOKEN`. Authentication is [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) over GitHub Actions OIDC: npm exchanges a short-lived token scoped to that one workflow run for publish rights, instead of a long-lived secret sitting in repo settings. That trust relationship is configured on the package's [npm settings page](https://www.npmjs.com/package/@epa-datos/ui/access) (provider GitHub Actions, repo `epa-datos/epa-ui`, workflow `publish.yml`) — if the workflow filename or repo ever changes, that config needs updating too, or CI publishing breaks.
|
|
101
|
+
|
|
102
|
+
The very first publish of a new package name/scope still has to be done manually (`npm publish --access public`) — Trusted Publishing can only be configured once the package already exists on the registry.
|
|
103
|
+
|
|
96
104
|
## License
|
|
97
105
|
|
|
98
106
|
MIT
|
|
@@ -30,6 +30,8 @@ type DateRangePickerPopoverProps = DateRangePickerBaseProps & {
|
|
|
30
30
|
compareValue?: DateRange;
|
|
31
31
|
onCompareValueChange?: (value: DateRange | undefined) => void;
|
|
32
32
|
compareOptions?: CompareOption[];
|
|
33
|
+
/** Muestra el interruptor "Comparar" y sus opciones. Ocúltalo cuando la vista ya maneja su propio comparativo. Default: true. */
|
|
34
|
+
showCompare?: boolean;
|
|
33
35
|
numberOfMonths?: number;
|
|
34
36
|
align?: React.ComponentProps<typeof PopoverContent>["align"];
|
|
35
37
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { differenceInCalendarDays, format, isSameDay, startOfToday, startOfYear, subDays, subMonths, subYears, } from "date-fns";
|
|
5
5
|
import { es } from "date-fns/locale";
|
|
@@ -92,7 +92,7 @@ function DateRangePicker(props) {
|
|
|
92
92
|
}
|
|
93
93
|
return _jsx(DateRangePickerPopover, { ...props });
|
|
94
94
|
}
|
|
95
|
-
function DateRangePickerPopover({ value, defaultValue, onValueChange, compareEnabled, defaultCompareEnabled = false, onCompareEnabledChange, compareValue, onCompareValueChange, presets = DEFAULT_PRESETS, compareOptions = DEFAULT_COMPARE_OPTIONS, numberOfMonths = 2, align = "end", className, }) {
|
|
95
|
+
function DateRangePickerPopover({ value, defaultValue, onValueChange, compareEnabled, defaultCompareEnabled = false, onCompareEnabledChange, compareValue, onCompareValueChange, presets = DEFAULT_PRESETS, compareOptions = DEFAULT_COMPARE_OPTIONS, showCompare = true, numberOfMonths = 2, align = "end", className, }) {
|
|
96
96
|
const [internalValue, setInternalValue] = React.useState(defaultValue ?? presets[3]?.getRange());
|
|
97
97
|
const appliedValue = value ?? internalValue;
|
|
98
98
|
const [internalCompareEnabled, setInternalCompareEnabled] = React.useState(defaultCompareEnabled);
|
|
@@ -153,20 +153,32 @@ function DateRangePickerPopover({ value, defaultValue, onValueChange, compareEna
|
|
|
153
153
|
: draftCompareEnabled
|
|
154
154
|
? draftCompareValue
|
|
155
155
|
: undefined;
|
|
156
|
-
function handleCalendarSelect(range) {
|
|
156
|
+
function handleCalendarSelect(range, triggerDate) {
|
|
157
|
+
// Con un rango completo, react-day-picker solo mueve el extremo final (o el
|
|
158
|
+
// inicial si el clic cae antes): desde "últimos 30 días" no hay forma de
|
|
159
|
+
// mover el inicio hacia adelante. Un clic sobre un rango ya completo
|
|
160
|
+
// empieza uno nuevo en esa fecha.
|
|
161
|
+
const next = calendarSelected?.from && calendarSelected.to
|
|
162
|
+
? { from: triggerDate, to: undefined }
|
|
163
|
+
: range;
|
|
157
164
|
if (editingTarget === "compare") {
|
|
158
|
-
setCustomCompareValue(
|
|
165
|
+
setCustomCompareValue(next);
|
|
159
166
|
}
|
|
160
167
|
else {
|
|
161
|
-
setDraftValue(
|
|
168
|
+
setDraftValue(next);
|
|
162
169
|
}
|
|
163
170
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
171
|
+
// Aplicar con un rango a medias cerraba el popover sin cambiar nada.
|
|
172
|
+
const canApply = Boolean(draftValue?.from && draftValue.to) &&
|
|
173
|
+
!(draftCompareEnabled &&
|
|
174
|
+
draftCompareType === "custom" &&
|
|
175
|
+
!(draftCompareValue?.from && draftCompareValue.to));
|
|
176
|
+
return (_jsxs(Popover, { open: open, onOpenChange: handleOpenChange, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "outline", className: cn("gap-1.5", className) }), children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), formatTriggerLabel(appliedValue, presets), appliedCompareEnabled && (_jsx("span", { className: "size-1.5 shrink-0 rounded-full bg-primary", "aria-hidden": "true" })), _jsx(ChevronDownIcon, { "data-icon": "inline-end", className: "text-muted-foreground" })] }), _jsxs(PopoverContent, { align: align, className: "w-auto flex-row gap-0 p-0", children: [_jsxs("div", { className: "flex w-48 shrink-0 flex-col gap-0.5 p-2", children: [presets.map((preset) => (_jsx("button", { type: "button", "data-active": rangesEqual(draftValue, preset.getRange()), onClick: () => handlePresetClick(preset), className: cn("w-full rounded-md px-2.5 py-1.5 text-left text-sm text-foreground transition-colors hover:bg-muted", "data-[active=true]:bg-muted data-[active=true]:font-medium data-[active=true]:text-primary"), children: preset.label }, preset.label))), showCompare && (_jsxs(_Fragment, { children: [_jsx(Separator, { className: "my-2" }), _jsxs("div", { className: "flex items-center justify-between px-2.5 py-1.5", children: [_jsx("span", { className: "text-sm font-medium", children: "Comparar" }), _jsx(Switch, { checked: draftCompareEnabled, onCheckedChange: (checked) => {
|
|
177
|
+
setDraftCompareEnabled(checked);
|
|
178
|
+
setEditingTarget(checked && draftCompareType === "custom" ? "compare" : "main");
|
|
179
|
+
} })] }), draftCompareEnabled &&
|
|
180
|
+
compareOptions.map((option) => (_jsx("button", { type: "button", "data-active": draftCompareType === option.value, onClick: () => handleCompareTypeClick(option.value), className: cn("w-full rounded-md px-2.5 py-1.5 text-left text-sm text-foreground transition-colors hover:bg-muted", "data-[active=true]:bg-muted data-[active=true]:font-medium data-[active=true]:text-primary"), children: option.label }, option.value)))] }))] }), _jsx(Separator, { orientation: "vertical" }), _jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [_jsxs("button", { type: "button", onClick: () => setEditingTarget("main"), className: cn("flex-1 rounded-lg border border-border px-3 py-2 text-left transition-colors", editingTarget === "main" && "border-primary"), children: [_jsx("span", { className: "text-[0.7rem] font-medium tracking-wide text-muted-foreground uppercase", children: "Per\u00EDodo" }), _jsx("p", { className: cn("text-sm font-medium", editingTarget === "main" && "text-primary"), children: formatRange(draftValue) })] }), draftCompareEnabled && (_jsxs("button", { type: "button", disabled: draftCompareType !== "custom", onClick: () => draftCompareType === "custom" && setEditingTarget("compare"), className: cn("flex-1 rounded-lg border border-border px-3 py-2 text-left transition-colors", draftCompareType === "custom" && "cursor-pointer", editingTarget === "compare" && "border-primary"), children: [_jsx("span", { className: "text-[0.7rem] font-medium tracking-wide text-muted-foreground uppercase", children: "Comparar" }), _jsx("p", { className: cn("text-sm font-medium", editingTarget === "compare" && "text-primary"), children: formatRange(draftCompareValue) })] }))] }), _jsx(Calendar, { mode: "range", locale: es, numberOfMonths: numberOfMonths, selected: calendarSelected, onSelect: handleCalendarSelect, defaultMonth: (editingTarget === "compare" ? draftCompareValue?.from : draftValue?.from) ??
|
|
181
|
+
undefined, modifiers: otherRange?.from && otherRange?.to ? { compare: otherRange } : undefined, modifiersClassNames: { compare: "bg-chart-2/15" }, className: "w-fit p-0" }), _jsx(Separator, {}), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", onClick: () => setOpen(false), children: "Cancelar" }), _jsx(Button, { onClick: handleApply, disabled: !canApply, children: "Aplicar" })] })] })] })] }));
|
|
170
182
|
}
|
|
171
183
|
function DateRangePickerInline({ value, defaultValue, onValueChange, presets = DEFAULT_PRESETS, className, }) {
|
|
172
184
|
const [internalValue, setInternalValue] = React.useState(defaultValue ?? presets[0]?.getRange());
|
|
@@ -3,11 +3,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
-
const toggleVariants = cva("group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-
|
|
6
|
+
const toggleVariants = cva("group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-primary/10 aria-pressed:font-semibold aria-pressed:text-primary data-[state=on]:bg-primary/10 data-[state=on]:text-primary dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
|
9
9
|
default: "bg-transparent",
|
|
10
|
-
outline: "border border-input bg-transparent hover:bg-muted",
|
|
10
|
+
outline: "border border-input bg-transparent hover:bg-muted aria-pressed:border-primary aria-pressed:hover:bg-primary/15",
|
|
11
11
|
},
|
|
12
12
|
size: {
|
|
13
13
|
default: "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epa-datos/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "React + Tailwind dashboard component library built on Base UI, published as source so it stays fully readable and themeable in the projects that consume it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "EPA Digital",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/epa-datos/epa-ui.git"
|
|
9
|
+
"url": "git+https://github.com/epa-datos/epa-ui.git"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/epa-datos/epa-ui#readme",
|
|
12
12
|
"bugs": {
|
|
@@ -70,9 +70,7 @@
|
|
|
70
70
|
"input-otp": "^1.4.2",
|
|
71
71
|
"lucide-react": "^1.31.0",
|
|
72
72
|
"next-themes": "^0.4.6",
|
|
73
|
-
"react": "19.2.8",
|
|
74
73
|
"react-day-picker": "^10.0.1",
|
|
75
|
-
"react-dom": "19.2.8",
|
|
76
74
|
"react-resizable-panels": "^4.12.2",
|
|
77
75
|
"recharts": "3.8.0",
|
|
78
76
|
"shadcn": "^4.17.0",
|
|
@@ -91,6 +89,8 @@
|
|
|
91
89
|
"eslint": "^9",
|
|
92
90
|
"eslint-config-next": "16.3.0",
|
|
93
91
|
"next": "16.3.0",
|
|
92
|
+
"react": "19.2.8",
|
|
93
|
+
"react-dom": "19.2.8",
|
|
94
94
|
"rimraf": "^6.0.1",
|
|
95
95
|
"tailwindcss": "^4",
|
|
96
96
|
"tsc-alias": "^1.8.10",
|