@pitchfork-ui/react 0.6.0 → 0.8.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.
Files changed (49) hide show
  1. package/dist/components/AvatarGroup/AvatarGroup.css +26 -0
  2. package/dist/components/AvatarGroup/AvatarGroup2.js +37 -0
  3. package/dist/components/Calendar/Calendar.css +0 -1
  4. package/dist/components/Combobox/Combobox.css +155 -0
  5. package/dist/components/Combobox/Combobox2.js +191 -0
  6. package/dist/components/CommandPalette/CommandPalette.css +225 -0
  7. package/dist/components/CommandPalette/CommandPalette2.js +195 -0
  8. package/dist/components/DateRangePicker/DateRangePicker.css +258 -0
  9. package/dist/components/DateRangePicker/DateRangePicker2.js +378 -0
  10. package/dist/components/Heatmap/Heatmap.css +4 -0
  11. package/dist/components/Icon/Icon2.js +43 -0
  12. package/dist/components/Kbd/Kbd.css +25 -0
  13. package/dist/components/Kbd/Kbd2.js +17 -0
  14. package/dist/components/NumberInput/NumberInput.css +98 -0
  15. package/dist/components/NumberInput/NumberInput2.js +165 -0
  16. package/dist/components/Pagination/Pagination.css +5 -2
  17. package/dist/components/Popover/Popover.css +46 -0
  18. package/dist/components/Popover/Popover2.js +76 -0
  19. package/dist/components/Toast/Toast.js +129 -0
  20. package/dist/index.cjs +1190 -24
  21. package/dist/index.js +9 -1
  22. package/dist/src/components/AvatarGroup/AvatarGroup.d.ts +14 -0
  23. package/dist/src/components/AvatarGroup/AvatarGroup.test.d.ts +1 -0
  24. package/dist/src/components/AvatarGroup/index.d.ts +1 -0
  25. package/dist/src/components/Combobox/Combobox.d.ts +20 -0
  26. package/dist/src/components/Combobox/Combobox.test.d.ts +1 -0
  27. package/dist/src/components/Combobox/index.d.ts +1 -0
  28. package/dist/src/components/CommandPalette/CommandPalette.d.ts +18 -0
  29. package/dist/src/components/CommandPalette/CommandPalette.test.d.ts +1 -0
  30. package/dist/src/components/CommandPalette/index.d.ts +1 -0
  31. package/dist/src/components/DateRangePicker/DateRangePicker.d.ts +21 -0
  32. package/dist/src/components/DateRangePicker/DateRangePicker.test.d.ts +1 -0
  33. package/dist/src/components/DateRangePicker/index.d.ts +1 -0
  34. package/dist/src/components/Kbd/Kbd.d.ts +9 -0
  35. package/dist/src/components/Kbd/Kbd.test.d.ts +1 -0
  36. package/dist/src/components/Kbd/index.d.ts +1 -0
  37. package/dist/src/components/NumberInput/NumberInput.d.ts +19 -0
  38. package/dist/src/components/NumberInput/NumberInput.test.d.ts +1 -0
  39. package/dist/src/components/NumberInput/index.d.ts +1 -0
  40. package/dist/src/components/Popover/Popover.d.ts +21 -0
  41. package/dist/src/components/Popover/Popover.test.d.ts +1 -0
  42. package/dist/src/components/Popover/index.d.ts +1 -0
  43. package/dist/src/components/Toast/Toast.d.ts +35 -0
  44. package/dist/src/components/Toast/Toast.test.d.ts +1 -0
  45. package/dist/src/components/Toast/index.d.ts +1 -0
  46. package/dist/src/index.d.ts +8 -0
  47. package/dist/styles/theme.css +68 -0
  48. package/dist/styles.css +986 -79
  49. package/package.json +1 -1
@@ -0,0 +1,165 @@
1
+ import { Keys, composeDescribedBy } from "../../a11y/index.js";
2
+ import { useComposedRefs } from "../../hooks/useComposedRefs.js";
3
+ import { useControllableState } from "../../hooks/useControllableState.js";
4
+ import { cx } from "../../utils/cx.js";
5
+ import { Icon } from "../Icon/Icon2.js";
6
+ import { FieldWrapper } from "../../utils/FieldWrapper.js";
7
+ import './NumberInput.css';/* empty css */
8
+ import { forwardRef, useId, useRef, useState } from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/components/NumberInput/NumberInput.tsx
11
+ var decimalsOf = (n) => {
12
+ const str = String(n);
13
+ const dot = str.indexOf(".");
14
+ return dot === -1 ? 0 : str.length - dot - 1;
15
+ };
16
+ var clamp = (n, min, max) => Math.min(Math.max(n, min), max);
17
+ var NumberInput = forwardRef(function NumberInput({ id, value, defaultValue, onValueChange, min = -Infinity, max = Infinity, step = 1, label, description, error, formatOptions, locale, decrementLabel = "Decrease", incrementLabel = "Increase", name, required, disabled, className, placeholder, "aria-describedby": ariaDescribedBy, ...props }, ref) {
18
+ const generatedId = useId();
19
+ const fieldId = id ?? generatedId;
20
+ const descriptionId = description ? `${fieldId}-description` : void 0;
21
+ const errorId = error ? `${fieldId}-error` : void 0;
22
+ const describedBy = composeDescribedBy(ariaDescribedBy, descriptionId, errorId);
23
+ const [rawValue, setCurrentValue] = useControllableState({
24
+ value,
25
+ defaultValue: defaultValue ?? null,
26
+ onChange: onValueChange
27
+ });
28
+ const currentValue = rawValue ?? null;
29
+ const inputRefs = useComposedRefs(useRef(null), ref);
30
+ const stepDecimals = decimalsOf(step);
31
+ const round = (n) => {
32
+ const factor = 10 ** stepDecimals;
33
+ return Math.round(n * factor) / factor;
34
+ };
35
+ const formatValue = (n) => {
36
+ if (n === null) return "";
37
+ if (formatOptions) return new Intl.NumberFormat(locale, formatOptions).format(n);
38
+ return String(n);
39
+ };
40
+ const [focused, setFocused] = useState(false);
41
+ const [draft, setDraft] = useState(() => formatValue(currentValue));
42
+ const displayValue = focused ? draft : formatValue(currentValue);
43
+ const commit = (next) => {
44
+ if (next === null) {
45
+ setCurrentValue(null);
46
+ setDraft("");
47
+ return;
48
+ }
49
+ const clamped = round(clamp(next, min, max));
50
+ setCurrentValue(clamped);
51
+ setDraft(focused ? String(clamped) : formatValue(clamped));
52
+ };
53
+ const stepBy = (direction) => {
54
+ if (disabled) return;
55
+ commit((currentValue ?? (Number.isFinite(min) ? min : Number.isFinite(max) ? max : 0)) + direction * step);
56
+ };
57
+ const atMin = currentValue !== null && currentValue <= min;
58
+ const atMax = currentValue !== null && currentValue >= max;
59
+ const onKeyDown = (event) => {
60
+ if (disabled) return;
61
+ if (event.key === Keys.ArrowUp) {
62
+ event.preventDefault();
63
+ stepBy(1);
64
+ } else if (event.key === Keys.ArrowDown) {
65
+ event.preventDefault();
66
+ stepBy(-1);
67
+ } else if (event.key === Keys.Home && Number.isFinite(min)) {
68
+ event.preventDefault();
69
+ commit(min);
70
+ } else if (event.key === Keys.End && Number.isFinite(max)) {
71
+ event.preventDefault();
72
+ commit(max);
73
+ }
74
+ };
75
+ return /* @__PURE__ */ jsx(FieldWrapper, {
76
+ labelFor: fieldId,
77
+ label,
78
+ description,
79
+ descriptionId,
80
+ error,
81
+ errorId,
82
+ required,
83
+ children: /* @__PURE__ */ jsxs("div", {
84
+ className: cx("pf-numberinput", error && "pf-numberinput--invalid"),
85
+ children: [
86
+ /* @__PURE__ */ jsx("button", {
87
+ type: "button",
88
+ className: "pf-numberinput__step pf-numberinput__step--decrement",
89
+ "aria-label": decrementLabel,
90
+ disabled: disabled || atMin,
91
+ tabIndex: -1,
92
+ onMouseDown: (event) => event.preventDefault(),
93
+ onClick: () => stepBy(-1),
94
+ children: /* @__PURE__ */ jsx(Icon, {
95
+ name: "minus",
96
+ "aria-hidden": true
97
+ })
98
+ }),
99
+ /* @__PURE__ */ jsx("input", {
100
+ ...props,
101
+ id: fieldId,
102
+ ref: inputRefs,
103
+ type: "text",
104
+ inputMode: "decimal",
105
+ role: "spinbutton",
106
+ className: cx("pf-numberinput__input", className),
107
+ value: displayValue,
108
+ placeholder,
109
+ disabled,
110
+ required,
111
+ autoComplete: "off",
112
+ "aria-invalid": error ? true : void 0,
113
+ "aria-describedby": describedBy,
114
+ "aria-valuenow": currentValue ?? void 0,
115
+ "aria-valuemin": Number.isFinite(min) ? min : void 0,
116
+ "aria-valuemax": Number.isFinite(max) ? max : void 0,
117
+ onFocus: () => {
118
+ setFocused(true);
119
+ setDraft(currentValue === null ? "" : String(currentValue));
120
+ },
121
+ onChange: (event) => {
122
+ const raw = event.target.value;
123
+ setDraft(raw);
124
+ if (raw.trim() === "") {
125
+ setCurrentValue(null);
126
+ return;
127
+ }
128
+ const parsed = Number(raw);
129
+ if (!Number.isNaN(parsed)) setCurrentValue(round(clamp(parsed, min, max)));
130
+ },
131
+ onBlur: () => {
132
+ setFocused(false);
133
+ if (draft.trim() === "") commit(null);
134
+ else {
135
+ const parsed = Number(draft);
136
+ commit(Number.isNaN(parsed) ? currentValue : parsed);
137
+ }
138
+ },
139
+ onKeyDown
140
+ }),
141
+ /* @__PURE__ */ jsx("button", {
142
+ type: "button",
143
+ className: "pf-numberinput__step pf-numberinput__step--increment",
144
+ "aria-label": incrementLabel,
145
+ disabled: disabled || atMax,
146
+ tabIndex: -1,
147
+ onMouseDown: (event) => event.preventDefault(),
148
+ onClick: () => stepBy(1),
149
+ children: /* @__PURE__ */ jsx(Icon, {
150
+ name: "plus",
151
+ "aria-hidden": true
152
+ })
153
+ }),
154
+ name ? /* @__PURE__ */ jsx("input", {
155
+ type: "hidden",
156
+ name,
157
+ value: currentValue ?? ""
158
+ }) : null
159
+ ]
160
+ })
161
+ });
162
+ });
163
+ NumberInput.displayName = "NumberInput";
164
+ //#endregion
165
+ export { NumberInput };
@@ -1,6 +1,7 @@
1
1
  .pf-pagination {
2
2
  align-items: center;
3
- display: inline-flex;
3
+ display: flex;
4
+ flex-wrap: wrap;
4
5
  gap: var(--space-2);
5
6
  }
6
7
 
@@ -16,7 +17,9 @@
16
17
 
17
18
  .pf-pagination__list {
18
19
  align-items: center;
19
- display: inline-flex;
20
+ display: flex;
21
+ flex-wrap: wrap;
22
+ justify-content: center;
20
23
  gap: var(--space-2);
21
24
  list-style: none;
22
25
  margin: 0;
@@ -0,0 +1,46 @@
1
+ .pf-popover {
2
+ position: fixed;
3
+ z-index: 1100;
4
+ max-width: min(360px, calc(100vw - 16px));
5
+ background: var(--pf-popover-bg);
6
+ border: 1px solid var(--pf-popover-border);
7
+ border-radius: var(--radius-md);
8
+ box-shadow: var(--pf-popover-elevation-popover-shadow, var(--pf-elevation-popover-shadow));
9
+ color: var(--pf-popover-text);
10
+ padding: var(--space-4);
11
+ transform-origin: top center;
12
+ animation: pf-popover-in var(--duration-fast) var(--easing-decelerate);
13
+ }
14
+
15
+ .pf-popover:focus-visible {
16
+ outline: none;
17
+ }
18
+
19
+ @keyframes pf-popover-in {
20
+ from {
21
+ opacity: 0;
22
+ transform: translateY(-4px) scale(0.97);
23
+ }
24
+ to {
25
+ opacity: 1;
26
+ transform: translateY(0) scale(1);
27
+ }
28
+ }
29
+
30
+ .pf-popover--exiting {
31
+ animation: pf-popover-out var(--duration-fast) var(--easing-accelerate) forwards;
32
+ }
33
+
34
+ @keyframes pf-popover-out {
35
+ to {
36
+ opacity: 0;
37
+ transform: translateY(-4px) scale(0.97);
38
+ }
39
+ }
40
+
41
+ @media (prefers-reduced-motion: reduce) {
42
+ .pf-popover,
43
+ .pf-popover--exiting {
44
+ animation: none;
45
+ }
46
+ }
@@ -0,0 +1,76 @@
1
+ import { Keys } from "../../a11y/index.js";
2
+ import { useAnchoredPosition } from "../../hooks/useAnchoredPosition.js";
3
+ import { useComposedRefs } from "../../hooks/useComposedRefs.js";
4
+ import { useControllableState } from "../../hooks/useControllableState.js";
5
+ import { useOutsideInteraction } from "../../hooks/useOutsideInteraction.js";
6
+ import { usePresence } from "../../hooks/usePresence.js";
7
+ import { cx } from "../../utils/cx.js";
8
+ import './Popover.css';/* empty css */
9
+ import { cloneElement, useEffect, useId, useRef } from "react";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { createPortal } from "react-dom";
12
+ //#region src/components/Popover/Popover.tsx
13
+ function Popover({ trigger, children, open, defaultOpen, onOpenChange, align = "start", label, closeOnOutsideClick = true, className }) {
14
+ const [isOpen = false, setOpen] = useControllableState({
15
+ value: open,
16
+ defaultValue: defaultOpen ?? false,
17
+ onChange: onOpenChange
18
+ });
19
+ const contentId = useId();
20
+ const triggerRef = useRef(null);
21
+ const contentRef = useRef(null);
22
+ const { isMounted, isExiting } = usePresence(isOpen, 160);
23
+ const style = useAnchoredPosition({
24
+ anchorRef: triggerRef,
25
+ floatingRef: contentRef,
26
+ enabled: isOpen,
27
+ align,
28
+ matchAnchorWidth: false,
29
+ flip: true
30
+ });
31
+ useOutsideInteraction({
32
+ refs: [triggerRef, contentRef],
33
+ enabled: isOpen,
34
+ onInteractOutside: () => {
35
+ if (closeOnOutsideClick) setOpen(false);
36
+ }
37
+ });
38
+ const wasOpen = useRef(false);
39
+ useEffect(() => {
40
+ if (isOpen) {
41
+ wasOpen.current = true;
42
+ contentRef.current?.focus();
43
+ } else if (wasOpen.current) {
44
+ wasOpen.current = false;
45
+ triggerRef.current?.focus();
46
+ }
47
+ }, [isOpen]);
48
+ const triggerProps = trigger.props;
49
+ return /* @__PURE__ */ jsxs(Fragment, { children: [cloneElement(trigger, {
50
+ ref: useComposedRefs(triggerRef, trigger.ref ?? triggerProps.ref),
51
+ "aria-haspopup": "dialog",
52
+ "aria-expanded": isOpen,
53
+ onClick: (event) => {
54
+ triggerProps.onClick?.(event);
55
+ setOpen(!isOpen);
56
+ }
57
+ }), isMounted && typeof document !== "undefined" ? createPortal(/* @__PURE__ */ jsx("div", {
58
+ ref: contentRef,
59
+ id: contentId,
60
+ role: "dialog",
61
+ "aria-label": label,
62
+ tabIndex: -1,
63
+ className: cx("pf-popover", isExiting && "pf-popover--exiting", className),
64
+ style,
65
+ onKeyDown: (event) => {
66
+ if (event.key === Keys.Escape) {
67
+ event.stopPropagation();
68
+ setOpen(false);
69
+ }
70
+ },
71
+ children
72
+ }), document.body) : null] });
73
+ }
74
+ Popover.displayName = "Popover";
75
+ //#endregion
76
+ export { Popover };
@@ -0,0 +1,129 @@
1
+ import { Notification, NotificationStack } from "../Notification/Notification2.js";
2
+ import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { createPortal } from "react-dom";
5
+ //#region src/components/Toast/Toast.tsx
6
+ var _emit = null;
7
+ function ToastItem({ entry, onRemove }) {
8
+ const { id, shouldExit } = entry;
9
+ const handleRemove = useCallback(() => onRemove(id), [onRemove, id]);
10
+ useEffect(() => {
11
+ if (!shouldExit) return;
12
+ const t = setTimeout(handleRemove, 270);
13
+ return () => clearTimeout(t);
14
+ }, [shouldExit, handleRemove]);
15
+ return /* @__PURE__ */ jsx(Notification, {
16
+ variant: entry.variant,
17
+ heading: entry.heading,
18
+ description: entry.description,
19
+ action: entry.action,
20
+ dismissible: entry.dismissible,
21
+ onDismiss: handleRemove,
22
+ className: shouldExit ? "pf-notification--exiting" : void 0,
23
+ onAnimationEnd: (e) => {
24
+ if (shouldExit && e.currentTarget === e.target) handleRemove();
25
+ }
26
+ });
27
+ }
28
+ function ToastProvider({ children, placement = "top-right", defaultDuration = 4e3 }) {
29
+ const [entries, setEntries] = useState([]);
30
+ const timers = useRef(/* @__PURE__ */ new Map());
31
+ const remove = useCallback((id) => {
32
+ timers.current.delete(id);
33
+ setEntries((prev) => prev.filter((e) => e.id !== id));
34
+ }, []);
35
+ const signalExit = useCallback((id) => {
36
+ timers.current.delete(id);
37
+ setEntries((prev) => prev.map((e) => e.id === id ? {
38
+ ...e,
39
+ shouldExit: true
40
+ } : e));
41
+ }, []);
42
+ const toast = useCallback((options) => {
43
+ const id = `t-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
44
+ const duration = options.duration !== void 0 ? options.duration : defaultDuration;
45
+ setEntries((prev) => [{
46
+ ...options,
47
+ id,
48
+ shouldExit: false,
49
+ dismissible: options.dismissible ?? true
50
+ }, ...prev]);
51
+ if (typeof duration === "number" && duration > 0) {
52
+ const t = setTimeout(() => signalExit(id), duration);
53
+ timers.current.set(id, t);
54
+ }
55
+ return id;
56
+ }, [defaultDuration, signalExit]);
57
+ const dismiss = useCallback((id) => {
58
+ const t = timers.current.get(id);
59
+ if (t) clearTimeout(t);
60
+ signalExit(id);
61
+ }, [signalExit]);
62
+ const dismissAll = useCallback(() => {
63
+ timers.current.forEach(clearTimeout);
64
+ timers.current.clear();
65
+ setEntries((prev) => prev.map((e) => ({
66
+ ...e,
67
+ shouldExit: true
68
+ })));
69
+ }, []);
70
+ useEffect(() => {
71
+ _emit = toast;
72
+ return () => {
73
+ if (_emit === toast) _emit = null;
74
+ };
75
+ }, [toast]);
76
+ useEffect(() => {
77
+ const map = timers.current;
78
+ return () => map.forEach(clearTimeout);
79
+ }, []);
80
+ return /* @__PURE__ */ jsxs(ToastContext.Provider, {
81
+ value: {
82
+ toast,
83
+ dismiss,
84
+ dismissAll
85
+ },
86
+ children: [children, typeof document !== "undefined" ? createPortal(/* @__PURE__ */ jsx(NotificationStack, {
87
+ placement,
88
+ children: entries.map((entry) => /* @__PURE__ */ jsx(ToastItem, {
89
+ entry,
90
+ onRemove: remove
91
+ }, entry.id))
92
+ }), document.body) : null]
93
+ });
94
+ }
95
+ var ToastContext = createContext(null);
96
+ function useToast() {
97
+ const ctx = useContext(ToastContext);
98
+ if (!ctx) throw new Error("useToast must be used inside <ToastProvider>.");
99
+ return ctx;
100
+ }
101
+ /**
102
+ * Fire a toast imperatively from anywhere — no hook needed.
103
+ * Requires a `<ToastProvider>` to be mounted somewhere in the tree.
104
+ */
105
+ function toast(options) {
106
+ if (!_emit) {
107
+ console.warn("[pitchfork-ui] toast() called before <ToastProvider> is mounted.");
108
+ return "";
109
+ }
110
+ return _emit(options);
111
+ }
112
+ toast.info = (opts) => toast({
113
+ ...opts,
114
+ variant: "info"
115
+ });
116
+ toast.success = (opts) => toast({
117
+ ...opts,
118
+ variant: "success"
119
+ });
120
+ toast.warning = (opts) => toast({
121
+ ...opts,
122
+ variant: "warning"
123
+ });
124
+ toast.danger = (opts) => toast({
125
+ ...opts,
126
+ variant: "danger"
127
+ });
128
+ //#endregion
129
+ export { ToastProvider, toast, useToast };