@shlinkio/shlink-frontend-kit 0.7.3 → 0.8.1

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
@@ -1,3 +1,15 @@
1
1
  # Shlink frontend kit
2
2
 
3
3
  React components and utilities for Shlink frontend projects
4
+
5
+ ## Tailwind alternatives
6
+
7
+ This library provides some tailwindcss-based components. To use them make sure to import components from `@shlinkio/shlink-frontend-kit/tailwind` and you add the following instructions to your tailwind stylesheet:
8
+
9
+ ```css
10
+ @import 'tailwindcss' prefix(tw);
11
+
12
+ /* Add these two lines */
13
+ @source '../node_modules/@shlinkio/shlink-frontend-kit';
14
+ @import '@shlinkio/shlink-frontend-kit/tailwind.preset.css';
15
+ ```
package/dist/index.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import { CardProps } from 'reactstrap';
2
2
  import { ChangeEvent } from 'react';
3
+ import { Dispatch } from 'react';
3
4
  import { DropdownToggleProps } from 'reactstrap/types/lib/DropdownToggle';
4
5
  import { FC } from 'react';
5
6
  import { JSX } from 'react/jsx-runtime';
6
7
  import { PropsWithChildren } from 'react';
7
8
  import { ReactNode } from 'react';
8
9
  import { RefObject } from 'react';
10
+ import { SetStateAction } from 'react';
9
11
 
10
12
  declare type BooleanControlProps = PropsWithChildren<{
11
13
  checked?: boolean;
@@ -193,12 +195,14 @@ declare type ToggleResult = [boolean, () => void, () => void, () => void];
193
195
 
194
196
  export declare const ToggleSwitch: FC<BooleanControlProps>;
195
197
 
196
- export declare const useElementRef: <T>() => RefObject<T | null>;
198
+ export declare const useElementRef: <T extends HTMLElement>() => RefObject<T | null>;
197
199
 
198
200
  export declare const useOrder: <T>(initialOrder: Order<T>) => [Order<T>, (orderField?: T, orderDir?: OrderDir) => void];
199
201
 
200
202
  export declare const useParsedQuery: <T>() => T;
201
203
 
204
+ export declare const useTheme: (initialTheme?: Theme) => readonly [Theme, Dispatch<SetStateAction<Theme>>];
205
+
202
206
  export declare const useTimeoutToggle: (initialValue?: boolean, delay?: number, setTimeout?: ((handler: TimerHandler, timeout?: number, ...arguments: any[]) => number) & typeof globalThis.setTimeout, clearTimeout?: ((id: number | undefined) => void) & typeof globalThis.clearTimeout) => [boolean, () => void];
203
207
 
204
208
  export declare const useToggle: (initialValue?: boolean) => ToggleResult;
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { jsx as o, jsxs as d, Fragment as N } from "react/jsx-runtime";
2
2
  import { faCircleNotch as I, faSearch as A, faEllipsisV as E, faSortAmountUp as F, faSortAmountDown as P } from "@fortawesome/free-solid-svg-icons";
3
- import { FontAwesomeIcon as b } from "@fortawesome/react-fontawesome";
3
+ import { FontAwesomeIcon as w } from "@fortawesome/react-fontawesome";
4
4
  import { clsx as u } from "clsx";
5
5
  import { Card as _, CardHeader as H, CardBody as M, Dropdown as $, DropdownToggle as k, DropdownMenu as S, NavItem as j, NavLink as G, Nav as U, UncontrolledDropdown as q, DropdownItem as g } from "reactstrap";
6
- import { useId as T, useState as w, useRef as C, useCallback as f, useMemo as Q, Children as V, isValidElement as Y } from "react";
7
- import { useLocation as W, NavLink as z } from "react-router";
8
- const de = ({ className: e, children: r, loading: t = !1, type: n = "default", fullWidth: c = !1 }) => /* @__PURE__ */ o(
6
+ import { useId as T, useState as p, useRef as C, useCallback as f, useMemo as Q, Children as V, isValidElement as Y, useEffect as W } from "react";
7
+ import { useLocation as z, NavLink as J } from "react-router";
8
+ const he = ({ className: e, children: r, loading: t = !1, type: n = "default", fullWidth: c = !1 }) => /* @__PURE__ */ o(
9
9
  _,
10
10
  {
11
11
  body: !0,
@@ -22,18 +22,18 @@ const de = ({ className: e, children: r, loading: t = !1, type: n = "default", f
22
22
  "text-danger": n === "error"
23
23
  }),
24
24
  children: [
25
- t && /* @__PURE__ */ o(b, { icon: I, spin: !0 }),
25
+ t && /* @__PURE__ */ o(w, { icon: I, spin: !0 }),
26
26
  t && /* @__PURE__ */ o("span", { className: "ms-2", children: r ?? "Loading..." }),
27
27
  !t && r
28
28
  ]
29
29
  }
30
30
  )
31
31
  }
32
- ), J = ({ title: e, children: r, bodyClassName: t, ...n }) => /* @__PURE__ */ d(_, { ...n, children: [
32
+ ), X = ({ title: e, children: r, bodyClassName: t, ...n }) => /* @__PURE__ */ d(_, { ...n, children: [
33
33
  e && /* @__PURE__ */ o(H, { role: "heading", "aria-level": 4, children: e }),
34
34
  /* @__PURE__ */ o(M, { className: t, children: r })
35
- ] }), me = ({ children: e, type: r, className: t, small: n = !1 }) => /* @__PURE__ */ o(
36
- J,
35
+ ] }), fe = ({ children: e, type: r, className: t, small: n = !1 }) => /* @__PURE__ */ o(
36
+ X,
37
37
  {
38
38
  role: "document",
39
39
  className: u("text-center", {
@@ -56,12 +56,12 @@ const de = ({ className: e, children: r, loading: t = !1, type: n = "default", f
56
56
  /* @__PURE__ */ o("input", { type: "checkbox", className: "form-check-input", id: l, checked: e, onChange: a }),
57
57
  /* @__PURE__ */ o("label", { className: "form-check-label", htmlFor: l, children: n })
58
58
  ] });
59
- }, ue = (e) => /* @__PURE__ */ o(x, { type: "checkbox", ...e }), he = (e) => /* @__PURE__ */ o(x, { type: "switch", ...e }), X = ({ children: e, label: r, className: t = "", labelClassName: n = "", noMargin: c = !1, id: s }) => /* @__PURE__ */ d("div", { className: `${t} ${c ? "" : "mb-3"}`, children: [
59
+ }, pe = (e) => /* @__PURE__ */ o(x, { type: "checkbox", ...e }), be = (e) => /* @__PURE__ */ o(x, { type: "switch", ...e }), Z = ({ children: e, label: r, className: t = "", labelClassName: n = "", noMargin: c = !1, id: s }) => /* @__PURE__ */ d("div", { className: `${t} ${c ? "" : "mb-3"}`, children: [
60
60
  /* @__PURE__ */ o("label", { className: `form-label ${n}`, htmlFor: s, children: r }),
61
61
  e
62
- ] }), fe = ({ children: e, value: r, onChange: t, type: n, required: c, placeholder: s, className: l, labelClassName: a }) => {
62
+ ] }), we = ({ children: e, value: r, onChange: t, type: n, required: c, placeholder: s, className: l, labelClassName: a }) => {
63
63
  const i = T();
64
- return /* @__PURE__ */ o(X, { label: /* @__PURE__ */ d(N, { children: [
64
+ return /* @__PURE__ */ o(Z, { label: /* @__PURE__ */ d(N, { children: [
65
65
  e,
66
66
  ":"
67
67
  ] }), className: l, labelClassName: a, id: i, children: /* @__PURE__ */ o(
@@ -76,13 +76,13 @@ const de = ({ className: e, children: r, loading: t = !1, type: n = "default", f
76
76
  onChange: (m) => t(m.target.value)
77
77
  }
78
78
  ) });
79
- }, Z = 500;
80
- let p;
79
+ }, B = 500;
80
+ let b;
81
81
  const v = () => {
82
- p !== null && clearTimeout(p), p = null;
83
- }, pe = ({ onChange: e, className: r, large: t = !0, noBorder: n = !1, initialValue: c = "", setTimeout_: s = setTimeout }) => {
84
- const [l, a] = w(c), i = (m, h = Z) => {
85
- a(m), v(), p = s(() => {
82
+ b !== null && clearTimeout(b), b = null;
83
+ }, ge = ({ onChange: e, className: r, large: t = !0, noBorder: n = !1, initialValue: c = "", setTimeout_: s = setTimeout }) => {
84
+ const [l, a] = p(c), i = (m, h = B) => {
85
+ a(m), v(), b = s(() => {
86
86
  e(m), v();
87
87
  }, h);
88
88
  };
@@ -100,7 +100,7 @@ const v = () => {
100
100
  onChange: (m) => i(m.target.value)
101
101
  }
102
102
  ),
103
- /* @__PURE__ */ o(b, { icon: A, className: "search-field__icon" }),
103
+ /* @__PURE__ */ o(w, { icon: A, className: "search-field__icon" }),
104
104
  /* @__PURE__ */ o(
105
105
  "button",
106
106
  {
@@ -113,7 +113,7 @@ const v = () => {
113
113
  }
114
114
  )
115
115
  ] });
116
- }, B = (e) => {
116
+ }, K = (e) => {
117
117
  const r = new URLSearchParams(e), t = {};
118
118
  return r.forEach((n, c) => {
119
119
  if (c.endsWith("[]")) {
@@ -122,23 +122,23 @@ const v = () => {
122
122
  } else
123
123
  t[c] = n;
124
124
  }), t;
125
- }, be = (e) => {
125
+ }, Ne = (e) => {
126
126
  const r = new URLSearchParams();
127
127
  for (const [t, n] of Object.entries(e))
128
128
  n !== void 0 && (Array.isArray(n) ? n.forEach((c) => r.append(`${t}[]`, `${c}`)) : r.append(t, `${n}`));
129
129
  return r.toString();
130
- }, K = (e = !1) => {
131
- const [r, t] = w(e), n = f(() => t((l) => !l), []), c = f(() => t(!0), []), s = f(() => t(!1), []);
130
+ }, ee = (e = !1) => {
131
+ const [r, t] = p(e), n = f(() => t((l) => !l), []), c = f(() => t(!0), []), s = f(() => t(!1), []);
132
132
  return [r, n, c, s];
133
- }, we = (e = !1, r = 2e3, t = window.setTimeout, n = window.clearTimeout) => {
134
- const [c, s] = w(e), l = C(e), a = C(void 0), i = f(() => {
133
+ }, Ce = (e = !1, r = 2e3, t = window.setTimeout, n = window.clearTimeout) => {
134
+ const [c, s] = p(e), l = C(e), a = C(void 0), i = f(() => {
135
135
  s(!l.current), a.current && n(a.current), a.current = t(() => s(l.current), r);
136
136
  }, [n, r, t]);
137
137
  return [c, i];
138
- }, ge = () => C(null), Ne = () => {
139
- const { search: e } = W();
140
- return Q(() => B(e), [e]);
141
- }, ee = ({
138
+ }, _e = () => C(null), Oe = () => {
139
+ const { search: e } = z();
140
+ return Q(() => K(e), [e]);
141
+ }, re = ({
142
142
  text: e,
143
143
  disabled: r = !1,
144
144
  className: t,
@@ -151,7 +151,7 @@ const v = () => {
151
151
  size: m,
152
152
  ...h
153
153
  }) => {
154
- const [y, L] = K(), D = u("dropdown-btn__toggle", t, {
154
+ const [y, L] = ee(), D = u("dropdown-btn__toggle", t, {
155
155
  "btn-block": !i,
156
156
  "dropdown-btn__toggle--with-caret": !s
157
157
  }), R = { minWidth: a && `${a}px` };
@@ -159,10 +159,10 @@ const v = () => {
159
159
  /* @__PURE__ */ o(k, { size: m, caret: !s, className: D, color: "primary", ...h, children: e }),
160
160
  /* @__PURE__ */ o(S, { className: "w-100", end: l, style: R, children: n })
161
161
  ] });
162
- }, Ce = ({ children: e, minWidth: r, label: t = "Options" }) => /* @__PURE__ */ o(
163
- ee,
162
+ }, ve = ({ children: e, minWidth: r, label: t = "Options" }) => /* @__PURE__ */ o(
163
+ re,
164
164
  {
165
- text: /* @__PURE__ */ o(b, { className: "px-1", icon: E }),
165
+ text: /* @__PURE__ */ o(w, { className: "px-1", icon: E }),
166
166
  "aria-label": t,
167
167
  size: "sm",
168
168
  minWidth: r,
@@ -171,8 +171,8 @@ const v = () => {
171
171
  inline: !0,
172
172
  children: e
173
173
  }
174
- ), re = ({ children: e, ...r }) => /* @__PURE__ */ o(j, { children: /* @__PURE__ */ o(G, { className: "nav-pills__nav-link", tag: z, ...r, children: e }) }), _e = ({ children: e, fill: r = !1, className: t = "" }) => /* @__PURE__ */ o(_, { className: `nav-pills__nav p-0 overflow-hidden ${t}`, body: !0, children: /* @__PURE__ */ o(U, { pills: !0, fill: r, children: V.map(e, (n) => {
175
- if (!Y(n) || n.type !== re)
174
+ ), te = ({ children: e, ...r }) => /* @__PURE__ */ o(j, { children: /* @__PURE__ */ o(G, { className: "nav-pills__nav-link", tag: J, ...r, children: e }) }), ke = ({ children: e, fill: r = !1, className: t = "" }) => /* @__PURE__ */ o(_, { className: `nav-pills__nav p-0 overflow-hidden ${t}`, body: !0, children: /* @__PURE__ */ o(U, { pills: !0, fill: r, children: V.map(e, (n) => {
175
+ if (!Y(n) || n.type !== te)
176
176
  throw new Error("Only NavPillItem children are allowed inside NavPills.");
177
177
  return n;
178
178
  }) }) });
@@ -182,23 +182,23 @@ function O(e, r, t) {
182
182
  DESC: void 0
183
183
  }[t] : "ASC";
184
184
  }
185
- function te(e, r, t) {
186
- return typeof e == "object" ? te(e.currentField, e.newField, e.currentOrderDir) : {
185
+ function ne(e, r, t) {
186
+ return typeof e == "object" ? ne(e.currentField, e.newField, e.currentOrderDir) : {
187
187
  field: r,
188
188
  dir: O(e, r, t)
189
189
  };
190
190
  }
191
- const Oe = (e, { field: r, dir: t }) => !r || !t ? e : e.sort((n, c) => {
191
+ const Se = (e, { field: r, dir: t }) => !r || !t ? e : e.sort((n, c) => {
192
192
  const s = t === "ASC" ? 1 : -1, l = t === "ASC" ? -1 : 1;
193
193
  return n[r] > c[r] ? s : l;
194
- }), ve = (e) => e.dir ? `${e.field}-${e.dir}` : void 0, ke = (e) => {
194
+ }), Te = (e) => e.dir ? `${e.field}-${e.dir}` : void 0, xe = (e) => {
195
195
  const [r, t] = e.split("-");
196
196
  return { field: r, dir: t };
197
- }, Se = (e) => {
198
- const [r, t] = w(e), n = f((c, s) => t({ field: c, dir: s }), []);
197
+ }, ye = (e) => {
198
+ const [r, t] = p(e), n = f((c, s) => t({ field: c, dir: s }), []);
199
199
  return [r, n];
200
200
  };
201
- function Te({ items: e, order: r, onChange: t, isButton: n = !0, right: c = !1, prefixed: s = !0 }) {
201
+ function Le({ items: e, order: r, onChange: t, isButton: n = !0, right: c = !1, prefixed: s = !0 }) {
202
202
  const l = (a) => () => {
203
203
  const i = O(a, r.field, r.dir);
204
204
  t(i ? a : void 0, i);
@@ -234,7 +234,7 @@ function Te({ items: e, order: r, onChange: t, isButton: n = !0, right: c = !1,
234
234
  className: "d-flex justify-content-between align-items-center",
235
235
  children: [
236
236
  i,
237
- r.field === a && /* @__PURE__ */ o(b, { icon: r.dir === "ASC" ? F : P })
237
+ r.field === a && /* @__PURE__ */ o(w, { icon: r.dir === "ASC" ? F : P })
238
238
  ]
239
239
  },
240
240
  a
@@ -244,46 +244,52 @@ function Te({ items: e, order: r, onChange: t, isButton: n = !0, right: c = !1,
244
244
  ] })
245
245
  ] });
246
246
  }
247
- const xe = "#4696e5", ye = "rgba(70, 150, 229, 0.4)", Le = "#f77f28", De = "rgba(247, 127, 40, 0.4)", Re = "white", Ie = "#161b22", Ae = (e) => {
247
+ const De = "#4696e5", Re = "rgba(70, 150, 229, 0.4)", Ie = "#f77f28", Ae = "rgba(247, 127, 40, 0.4)", Ee = "white", Fe = "#161b22", oe = (e) => {
248
248
  var r;
249
249
  return (r = document.querySelector("html")) == null ? void 0 : r.setAttribute("data-theme", e);
250
- }, Ee = () => {
250
+ }, Pe = () => {
251
251
  var e;
252
252
  return ((e = document.querySelector("html")) == null ? void 0 : e.getAttribute("data-theme")) === "dark";
253
- }, Fe = (e = window.matchMedia.bind(window)) => e("(prefers-color-scheme: dark)").matches ? "dark" : "light";
253
+ }, ce = (e = window.matchMedia.bind(window)) => e("(prefers-color-scheme: dark)").matches ? "dark" : "light", He = (e) => {
254
+ const [r, t] = p(() => e ?? ce());
255
+ return W(() => {
256
+ oe(r);
257
+ }, [r]), [r, t];
258
+ };
254
259
  export {
255
- ue as Checkbox,
256
- ee as DropdownBtn,
257
- Le as HIGHLIGHTED_COLOR,
258
- De as HIGHLIGHTED_COLOR_ALPHA,
259
- fe as InputFormGroup,
260
- X as LabeledFormGroup,
261
- xe as MAIN_COLOR,
262
- ye as MAIN_COLOR_ALPHA,
263
- de as Message,
264
- re as NavPillItem,
265
- _e as NavPills,
266
- Te as OrderingDropdown,
267
- Ie as PRIMARY_DARK_COLOR,
268
- Re as PRIMARY_LIGHT_COLOR,
269
- me as Result,
270
- Ce as RowDropdownBtn,
271
- pe as SearchField,
272
- J as SimpleCard,
273
- he as ToggleSwitch,
274
- Ae as changeThemeInMarkup,
275
- te as determineOrder,
260
+ pe as Checkbox,
261
+ re as DropdownBtn,
262
+ Ie as HIGHLIGHTED_COLOR,
263
+ Ae as HIGHLIGHTED_COLOR_ALPHA,
264
+ we as InputFormGroup,
265
+ Z as LabeledFormGroup,
266
+ De as MAIN_COLOR,
267
+ Re as MAIN_COLOR_ALPHA,
268
+ he as Message,
269
+ te as NavPillItem,
270
+ ke as NavPills,
271
+ Le as OrderingDropdown,
272
+ Fe as PRIMARY_DARK_COLOR,
273
+ Ee as PRIMARY_LIGHT_COLOR,
274
+ fe as Result,
275
+ ve as RowDropdownBtn,
276
+ ge as SearchField,
277
+ X as SimpleCard,
278
+ be as ToggleSwitch,
279
+ oe as changeThemeInMarkup,
280
+ ne as determineOrder,
276
281
  O as determineOrderDir,
277
- Fe as getSystemPreferredTheme,
278
- Ee as isDarkThemeEnabled,
279
- ve as orderToString,
280
- B as parseQueryString,
281
- Oe as sortList,
282
- ke as stringToOrder,
283
- be as stringifyQueryParams,
284
- ge as useElementRef,
285
- Se as useOrder,
286
- Ne as useParsedQuery,
287
- we as useTimeoutToggle,
288
- K as useToggle
282
+ ce as getSystemPreferredTheme,
283
+ Pe as isDarkThemeEnabled,
284
+ Te as orderToString,
285
+ K as parseQueryString,
286
+ Se as sortList,
287
+ xe as stringToOrder,
288
+ Ne as stringifyQueryParams,
289
+ _e as useElementRef,
290
+ ye as useOrder,
291
+ Oe as useParsedQuery,
292
+ He as useTheme,
293
+ Ce as useTimeoutToggle,
294
+ ee as useToggle
289
295
  };
@@ -0,0 +1,203 @@
1
+ import { FC } from 'react';
2
+ import { HTMLProps } from 'react';
3
+ import { InputHTMLAttributes } from 'react';
4
+ import { LinkProps } from 'react-router';
5
+ import { PropsWithChildren } from 'react';
6
+ import { ReactNode } from 'react';
7
+
8
+ export declare type BaseInputProps = {
9
+ size?: Size;
10
+ feedback?: 'error';
11
+ };
12
+
13
+ export declare const Button: FC<ButtonProps>;
14
+
15
+ export declare type ButtonProps = PropsWithChildren<{
16
+ disabled?: boolean;
17
+ className?: string;
18
+ variant?: 'primary' | 'secondary' | 'danger';
19
+ size?: Size;
20
+ inline?: boolean;
21
+ solid?: boolean;
22
+ } & (RegularButtonProps | LinkButtonProps_2)>;
23
+
24
+ export declare type Callback = () => unknown;
25
+
26
+ export declare const Card: FC<CardProps> & {
27
+ Body: FC<CardProps>;
28
+ Header: FC<CardProps>;
29
+ Footer: FC<CardProps>;
30
+ };
31
+
32
+ export declare type CardProps = HTMLProps<HTMLDivElement>;
33
+
34
+ export declare type CellProps = HTMLProps<HTMLTableCellElement> & {
35
+ /**
36
+ * The name of the column to be displayed in small resolutions when the table is responsive, where the cells collapse.
37
+ * It is ignored for non-responsive tables.
38
+ */
39
+ columnName?: string;
40
+ /**
41
+ * Whether to use a th or td tag. If not provided, it is inferred based on the section, using td when inside tbody,
42
+ * and th when inside thead or tfoot
43
+ */
44
+ type?: 'td' | 'th';
45
+ };
46
+
47
+ export declare const CloseButton: FC<CloseButtonProps>;
48
+
49
+ export declare type CloseButtonProps = {
50
+ label?: string;
51
+ onClick?: HTMLProps<HTMLButtonElement>['onClick'];
52
+ };
53
+
54
+ export declare const ELLIPSIS = "...";
55
+
56
+ declare type Ellipsis = typeof ELLIPSIS;
57
+
58
+ export declare const formatNumber: (number: number | string) => string;
59
+
60
+ export declare const Input: FC<InputProps>;
61
+
62
+ export declare type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> & BaseInputProps & {
63
+ borderless?: boolean;
64
+ };
65
+
66
+ export declare const keyForPage: (pageNumber: NumberOrEllipsis, index: number) => string;
67
+
68
+ export declare const Label: FC<LabelProps>;
69
+
70
+ export declare const LabelledInput: FC<LabelledInputProps>;
71
+
72
+ export declare type LabelledInputProps = Omit<InputProps, 'className' | 'id' | 'feedback'> & {
73
+ label: string;
74
+ inputClassName?: string;
75
+ error?: string;
76
+ /** Alternative to `required`. Causes the input to be required, without displaying an asterisk */
77
+ hiddenRequired?: boolean;
78
+ };
79
+
80
+ export declare const LabelledSelect: FC<LabelledSelectProps>;
81
+
82
+ export declare type LabelledSelectProps = Omit<SelectProps, 'className' | 'id'> & {
83
+ label: string;
84
+ selectClassName?: string;
85
+ /** Alternative to `required`. Causes the input to be required, without displaying an asterisk */
86
+ hiddenRequired?: boolean;
87
+ };
88
+
89
+ export declare type LabelProps = HTMLProps<HTMLLabelElement> & {
90
+ required?: boolean;
91
+ };
92
+
93
+ export declare const LinkButton: FC<LinkButtonProps>;
94
+
95
+ export declare type LinkButtonProps = Omit<HTMLProps<HTMLButtonElement>, 'size' | 'type'> & {
96
+ size?: Size;
97
+ type?: HTMLButtonElement['type'];
98
+ };
99
+
100
+ declare type LinkButtonProps_2 = LinkProps;
101
+
102
+ export declare const ModalDialog: FC<ModalDialogProps>;
103
+
104
+ export declare type ModalDialogProps = HTMLProps<HTMLDialogElement> & {
105
+ /** Whether the dialog is open or not */
106
+ open: boolean;
107
+ /** Invoked when the dialog is closed for any reason */
108
+ onClose: () => void;
109
+ };
110
+
111
+ declare type NoTitleProps = {
112
+ title?: never;
113
+ titleSize?: never;
114
+ };
115
+
116
+ export declare type NumberOrEllipsis = number | Ellipsis;
117
+
118
+ export declare const pageIsEllipsis: (pageNumber: NumberOrEllipsis) => pageNumber is Ellipsis;
119
+
120
+ export declare const Paginator: FC<PaginatorProps>;
121
+
122
+ export declare type PaginatorProps = {
123
+ pagesCount: number;
124
+ currentPage: number;
125
+ } & ({
126
+ onPageChange: (currentPage: number) => void;
127
+ } | {
128
+ urlForPage: (pageNumber: number) => string;
129
+ });
130
+
131
+ export declare const prettifyPageNumber: (pageNumber: NumberOrEllipsis) => string;
132
+
133
+ export declare const progressivePagination: (currentPage: number, pageCount: number) => NumberOrEllipsis[];
134
+
135
+ declare type RegularButtonProps = Omit<HTMLProps<HTMLButtonElement>, 'size'>;
136
+
137
+ export declare const roundTen: (number: number) => number;
138
+
139
+ export declare const SearchInput: FC<SearchInputProps>;
140
+
141
+ export declare type SearchInputProps = Omit<InputProps, 'className' | 'onChange' | 'value'> & {
142
+ onChange: (searchTerm: string) => void;
143
+ containerClassName?: string;
144
+ inputClassName?: string;
145
+ };
146
+
147
+ export declare type SectionType = 'head' | 'body' | 'footer';
148
+
149
+ export declare const Select: FC<SelectProps>;
150
+
151
+ declare type SelectElementProps = Omit<HTMLProps<HTMLSelectElement>, 'size'>;
152
+
153
+ export declare type SelectProps = PropsWithChildren<SelectElementProps & BaseInputProps>;
154
+
155
+ export declare const SimpleCard: FC<SimpleCardProps>;
156
+
157
+ export declare type SimpleCardProps = Omit<CardProps, 'title' | 'size'> & {
158
+ bodyClassName?: string;
159
+ } & (TitleProps | NoTitleProps);
160
+
161
+ declare type Size = 'sm' | 'md' | 'lg';
162
+
163
+ export declare const Table: FC<TableProps> & {
164
+ Row: FC<HTMLProps<HTMLTableRowElement>>;
165
+ Cell: FC<CellProps>;
166
+ };
167
+
168
+ export declare type TableElementProps = PropsWithChildren & {
169
+ className?: string;
170
+ };
171
+
172
+ export declare type TableProps = HTMLProps<HTMLTableElement> & {
173
+ header: ReactNode;
174
+ footer?: ReactNode;
175
+ /**
176
+ * By default, the table rows will collapse under large resolutions, and the headers will be hidden.
177
+ * Set `responsive={false}` to avoid this behavior.
178
+ */
179
+ responsive?: boolean;
180
+ };
181
+
182
+ declare type TitleProps = {
183
+ title: ReactNode;
184
+ titleSize?: Size;
185
+ };
186
+
187
+ export declare function useTimeout(defaultDelay: number,
188
+ /** Test seam. Defaults to global setTimeout */
189
+ setTimeout_?: typeof globalThis.setTimeout,
190
+ /** Test seam. Defaults to global clearTimeout */
191
+ clearTimeout_?: typeof clearTimeout): UseTimeoutResult;
192
+
193
+ export declare type UseTimeoutResult = {
194
+ /**
195
+ * Clears any in-progress timeout, and schedules a new callback.
196
+ * It optionally allows a delay to be provided, or falls back to the default delay otherwise.
197
+ */
198
+ setTimeout: (callback: Callback, delay?: number) => void;
199
+ /** Clears any in-progress timeout */
200
+ clearCurrentTimeout: () => void;
201
+ };
202
+
203
+ export { }
@@ -0,0 +1,494 @@
1
+ import { jsx as n, jsxs as c } from "react/jsx-runtime";
2
+ import a from "clsx";
3
+ import { createContext as C, useContext as m, useRef as S, useEffect as B, useId as L, useCallback as f, useMemo as v } from "react";
4
+ import { Link as R } from "react-router";
5
+ import { faClose as U, faSearch as A, faChevronLeft as T, faChevronRight as I } from "@fortawesome/free-solid-svg-icons";
6
+ import { FontAwesomeIcon as b } from "@fortawesome/react-fontawesome";
7
+ const p = C(void 0), h = C({ responsive: !0 }), D = ({ children: t, className: r }) => {
8
+ const { responsive: e } = m(h);
9
+ return /* @__PURE__ */ n(p.Provider, { value: { section: "head" }, children: /* @__PURE__ */ n(
10
+ "thead",
11
+ {
12
+ className: a(
13
+ { "tw:hidden tw:lg:table-header-group": e },
14
+ r
15
+ ),
16
+ children: t
17
+ }
18
+ ) });
19
+ }, O = ({ children: t, className: r }) => {
20
+ const { responsive: e } = m(h);
21
+ return /* @__PURE__ */ n(p.Provider, { value: { section: "body" }, children: /* @__PURE__ */ n(
22
+ "tbody",
23
+ {
24
+ className: a(
25
+ { "tw:lg:table-row-group tw:flex tw:flex-col tw:gap-y-3": e },
26
+ r
27
+ ),
28
+ children: t
29
+ }
30
+ ) });
31
+ }, W = ({ children: t, className: r }) => {
32
+ const { responsive: e } = m(h);
33
+ return /* @__PURE__ */ n(p.Provider, { value: { section: "footer" }, children: /* @__PURE__ */ n(
34
+ "tfoot",
35
+ {
36
+ className: a(
37
+ { "tw:lg:table-row-group tw:flex tw:flex-col tw:gap-y-3 tw:mt-4": e },
38
+ r
39
+ ),
40
+ children: t
41
+ }
42
+ ) });
43
+ }, _ = ({ children: t, className: r, ...e }) => {
44
+ const o = m(p), d = (o == null ? void 0 : o.section) === "body", { responsive: l } = m(h);
45
+ return /* @__PURE__ */ n(
46
+ "tr",
47
+ {
48
+ className: a(
49
+ "tw:group",
50
+ {
51
+ "tw:lg:table-row tw:flex tw:flex-col": l,
52
+ "tw:lg:border-0 tw:border-y-2 tw:border-lm-border tw:dark:border-dm-border": l,
53
+ "tw:hover:bg-lm-primary tw:dark:hover:bg-dm-primary": d,
54
+ // Use a different hover bg color depending on the table being inside a card or not
55
+ "tw:group-[&]/card:hover:bg-lm-secondary tw:dark:group-[&]/card:hover:bg-dm-secondary": d
56
+ },
57
+ r
58
+ ),
59
+ ...e,
60
+ children: t
61
+ }
62
+ );
63
+ }, q = ({ children: t, className: r, columnName: e, type: o, ...d }) => {
64
+ const l = m(p), s = o ?? ((l == null ? void 0 : l.section) !== "body" ? "th" : "td"), { responsive: i } = m(h);
65
+ return /* @__PURE__ */ n(
66
+ s,
67
+ {
68
+ "data-column": i ? e : void 0,
69
+ className: a(
70
+ "tw:p-2 tw:border-lm-border tw:dark:border-dm-border",
71
+ {
72
+ "tw:border-b-1": !i,
73
+ "tw:block tw:lg:table-cell tw:not-last:border-b-1 tw:lg:border-b-1": i,
74
+ // For md and lower, display the content in data-column attribute as before
75
+ "tw:before:lg:hidden tw:before:content-[attr(data-column)] tw:before:font-bold tw:before:mr-1": i && s === "td"
76
+ },
77
+ r
78
+ ),
79
+ ...d,
80
+ children: t
81
+ }
82
+ );
83
+ }, G = ({ header: t, footer: r, children: e, responsive: o = !0, ...d }) => /* @__PURE__ */ n(h.Provider, { value: { responsive: o }, children: /* @__PURE__ */ c("table", { className: "tw:w-full", ...d, children: [
84
+ /* @__PURE__ */ n(D, { children: t }),
85
+ /* @__PURE__ */ n(O, { children: e }),
86
+ r && /* @__PURE__ */ n(W, { children: r })
87
+ ] }) }), bt = Object.assign(G, { Row: _, Cell: q }), ht = ({
88
+ open: t,
89
+ children: r,
90
+ className: e,
91
+ ...o
92
+ }) => {
93
+ const d = S(null);
94
+ return B(() => {
95
+ var w, N;
96
+ const l = document.body, s = l.style.overflow, i = l.style.paddingRight;
97
+ if (t) {
98
+ const $ = window.outerWidth - l.clientWidth, z = l.scrollHeight > l.clientHeight;
99
+ l.style.overflow = "hidden", z && (l.style.paddingRight = `${$}px`), (w = d.current) == null || w.showModal();
100
+ } else
101
+ (N = d.current) == null || N.close();
102
+ return () => {
103
+ l.style.overflow = s, l.style.paddingRight = i;
104
+ };
105
+ }, [t]), /* @__PURE__ */ n(
106
+ "dialog",
107
+ {
108
+ ref: d,
109
+ className: a("tw:bg-transparent tw:backdrop:bg-black/50", e),
110
+ ...o,
111
+ children: t && r
112
+ }
113
+ );
114
+ }, ut = ({
115
+ children: t,
116
+ className: r,
117
+ disabled: e,
118
+ variant: o = "primary",
119
+ size: d = "md",
120
+ inline: l = !1,
121
+ solid: s = !1,
122
+ ...i
123
+ }) => {
124
+ const w = "to" in i ? R : "button";
125
+ return (
126
+ // @ts-expect-error We are explicitly checking for the `to` prop before using Link
127
+ /* @__PURE__ */ n(
128
+ w,
129
+ {
130
+ className: a(
131
+ {
132
+ "tw:inline-flex": l,
133
+ "tw:flex": !l
134
+ },
135
+ "tw:gap-2 tw:items-center tw:justify-center",
136
+ "tw:border tw:rounded-md tw:no-underline",
137
+ "tw:transition-colors",
138
+ {
139
+ "tw:focus-ring": o !== "danger",
140
+ "tw:focus-ring-danger": o === "danger"
141
+ },
142
+ {
143
+ "tw:px-1.5 tw:py-1 tw:text-sm": d === "sm",
144
+ "tw:px-3 tw:py-1.5": d === "md",
145
+ "tw:px-4 tw:py-2 tw:text-lg": d === "lg"
146
+ },
147
+ {
148
+ "tw:border-brand tw:text-brand": o === "primary",
149
+ "tw:border-zinc-500": o === "secondary",
150
+ "tw:text-zinc-500": o === "secondary" && !s,
151
+ "tw:border-danger": o === "danger",
152
+ "tw:text-danger": o === "danger" && !s
153
+ },
154
+ s && {
155
+ "tw:text-white": !0,
156
+ "tw:bg-brand": o === "primary",
157
+ "tw:highlight:bg-brand-dark tw:highlight:border-brand-dark": o === "primary",
158
+ "tw:bg-zinc-500": o === "secondary",
159
+ "tw:highlight:bg-zinc-600 tw:highlight:border-zinc-600": o === "secondary",
160
+ "tw:bg-danger": o === "danger",
161
+ "tw:highlight:bg-danger-dark tw:highlight:border-danger-dark": o === "danger"
162
+ },
163
+ !e && {
164
+ "tw:highlight:text-white": !s,
165
+ "tw:highlight:bg-brand": o === "primary",
166
+ "tw:highlight:bg-zinc-500": o === "secondary",
167
+ "tw:highlight:bg-danger": o === "danger"
168
+ },
169
+ {
170
+ "tw:pointer-events-none tw:opacity-65": e
171
+ },
172
+ r
173
+ ),
174
+ disabled: e,
175
+ ...i,
176
+ children: t
177
+ }
178
+ )
179
+ );
180
+ }, pt = ({ onClick: t, label: r = "Close" }) => /* @__PURE__ */ n(
181
+ "button",
182
+ {
183
+ onClick: t,
184
+ className: a(
185
+ "tw:opacity-50 tw:highlight:opacity-80 tw:transition-opacity",
186
+ "tw:rounded-md tw:focus-ring"
187
+ ),
188
+ "aria-label": r,
189
+ children: /* @__PURE__ */ n(b, { icon: U, size: "xl" })
190
+ }
191
+ ), F = ({
192
+ borderless: t = !1,
193
+ size: r = "md",
194
+ feedback: e,
195
+ className: o,
196
+ disabled: d,
197
+ readOnly: l,
198
+ ...s
199
+ }) => {
200
+ const i = !d && !l;
201
+ return /* @__PURE__ */ n(
202
+ "input",
203
+ {
204
+ className: a(
205
+ "tw:w-full",
206
+ {
207
+ "tw:focus-ring": !e,
208
+ "tw:focus-ring-danger": e === "error"
209
+ },
210
+ {
211
+ "tw:px-2 tw:py-1 tw:text-sm": r === "sm",
212
+ "tw:px-3 tw:py-1.5": r === "md",
213
+ "tw:px-4 tw:py-2 tw:text-xl": r === "lg"
214
+ },
215
+ {
216
+ "tw:rounded-md tw:border": !t,
217
+ "tw:border-lm-input-border tw:dark:border-dm-input-border": !t && !e,
218
+ "tw:border-danger": !t && e === "error",
219
+ "tw:bg-lm-disabled-input tw:dark:bg-dm-disabled-input": !i,
220
+ "tw:bg-lm-primary tw:dark:bg-dm-primary": i,
221
+ // Use different background color when rendered inside a card
222
+ "tw:group-[&]/card:bg-lm-input tw:group-[&]/card:dark:bg-dm-input": i
223
+ },
224
+ o
225
+ ),
226
+ disabled: d,
227
+ readOnly: l,
228
+ ...s
229
+ }
230
+ );
231
+ }, E = ({ required: t, children: r, ...e }) => /* @__PURE__ */ c("label", { ...e, children: [
232
+ r,
233
+ t && /* @__PURE__ */ n("span", { className: "tw:text-danger tw:ml-1", "data-testid": "required-indicator", children: "*" })
234
+ ] }), ft = ({ label: t, inputClassName: r, required: e, hiddenRequired: o, error: d, ...l }) => {
235
+ const s = L();
236
+ return /* @__PURE__ */ c("div", { className: "tw:flex tw:flex-col tw:gap-1", children: [
237
+ /* @__PURE__ */ n(E, { htmlFor: s, required: e, children: t }),
238
+ /* @__PURE__ */ n(
239
+ F,
240
+ {
241
+ id: s,
242
+ className: r,
243
+ required: e || o,
244
+ feedback: d ? "error" : void 0,
245
+ ...l
246
+ }
247
+ ),
248
+ d && /* @__PURE__ */ n("span", { className: "tw:text-danger", children: d })
249
+ ] });
250
+ }, J = String.raw`data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>`, K = ({
251
+ className: t,
252
+ size: r = "md",
253
+ feedback: e,
254
+ style: o = {},
255
+ disabled: d,
256
+ ...l
257
+ }) => /* @__PURE__ */ n(
258
+ "select",
259
+ {
260
+ className: a(
261
+ "tw:w-full tw:appearance-none tw:pr-9 tw:bg-no-repeat",
262
+ {
263
+ "tw:focus-ring": !e,
264
+ "tw:focus-ring-danger": e === "error"
265
+ },
266
+ "tw:rounded-md tw:border",
267
+ {
268
+ "tw:border-lm-input-border tw:dark:border-dm-input-border": !e,
269
+ "tw:border-danger": e === "error"
270
+ },
271
+ {
272
+ "tw:pl-2 tw:py-1 tw:text-sm": r === "sm",
273
+ "tw:pl-3 tw:py-1.5": r === "md",
274
+ "tw:pl-4 tw:py-2 tw:text-xl": r === "lg",
275
+ "tw:bg-lm-disabled-input tw:dark:bg-dm-disabled-input": d,
276
+ // Apply different background color when rendered inside a card
277
+ "tw:bg-lm-primary tw:dark:bg-dm-primary tw:group-[&]/card:bg-lm-input tw:group-[&]/card:dark:bg-dm-input": !d
278
+ },
279
+ t
280
+ ),
281
+ style: {
282
+ ...o,
283
+ backgroundImage: `url("${J}")`,
284
+ backgroundSize: "16px 12px",
285
+ backgroundPosition: "right 0.75rem center"
286
+ },
287
+ disabled: d,
288
+ ...l
289
+ }
290
+ ), yt = ({ selectClassName: t, label: r, required: e, hiddenRequired: o, ...d }) => {
291
+ const l = L();
292
+ return /* @__PURE__ */ c("div", { className: "tw:flex tw:flex-col tw:gap-1", children: [
293
+ /* @__PURE__ */ n(E, { htmlFor: l, required: e, children: r }),
294
+ /* @__PURE__ */ n(K, { id: l, className: t, required: e || o, ...d })
295
+ ] });
296
+ };
297
+ function Q(t, r = globalThis.setTimeout.bind(globalThis), e = globalThis.clearTimeout.bind(globalThis)) {
298
+ const o = S(null), d = f(() => {
299
+ o.current && e(o.current);
300
+ }, [e]), l = f((s, i) => {
301
+ d(), o.current = r(() => {
302
+ s(), o.current = null;
303
+ }, i ?? t);
304
+ }, [d, t, r]);
305
+ return B(() => d, [d]), v(
306
+ () => ({ setTimeout: l, clearCurrentTimeout: d }),
307
+ [d, l]
308
+ );
309
+ }
310
+ const xt = ({
311
+ onChange: t,
312
+ containerClassName: r,
313
+ inputClassName: e,
314
+ // Inputs have a default 'md' size. Search inputs are usually 'lg' as they are rendered at the top of sections
315
+ size: o = "lg",
316
+ ...d
317
+ }) => {
318
+ const { setTimeout: l, clearCurrentTimeout: s } = Q(500), i = f((w) => {
319
+ w ? l(() => t(w)) : (s(), t(w));
320
+ }, [s, t, l]);
321
+ return /* @__PURE__ */ c("div", { className: a("tw:group tw:relative tw:focus-within:z-10", r), children: [
322
+ /* @__PURE__ */ n(
323
+ b,
324
+ {
325
+ icon: A,
326
+ className: a(
327
+ "tw:absolute tw:top-[50%] tw:translate-y-[-50%] tw:transition-colors",
328
+ "tw:text-placeholder tw:group-focus-within:text-lm-text tw:dark:group-focus-within:text-dm-text",
329
+ {
330
+ "tw:left-3": o !== "sm",
331
+ "tw:scale-85 tw:left-2": o === "sm"
332
+ }
333
+ )
334
+ }
335
+ ),
336
+ /* @__PURE__ */ n(
337
+ F,
338
+ {
339
+ type: "search",
340
+ className: a(
341
+ {
342
+ "tw:pl-9": o !== "sm",
343
+ "tw:pl-7": o === "sm"
344
+ },
345
+ e
346
+ ),
347
+ placeholder: "Search...",
348
+ onChange: (w) => i(w.target.value),
349
+ size: o,
350
+ ...d
351
+ }
352
+ )
353
+ ] });
354
+ }, V = new Intl.NumberFormat("en-US"), X = (t) => V.format(Number(t)), P = 10, kt = (t) => Math.ceil(t / P) * P, g = 2, y = "...", Y = (t, r) => Array.from({ length: r - t }, (e, o) => t + o), Z = (t, r) => {
355
+ const e = Y(
356
+ Math.max(g, t - g),
357
+ Math.min(r - 1, t + g) + 1
358
+ );
359
+ return t - g > g && e.unshift(y), t + g < r - 1 && e.push(y), e.unshift(1), e.push(r), e;
360
+ }, u = (t) => t === y, tt = (t) => u(t) ? t : X(t), rt = (t, r) => u(t) ? `${t}_${r}` : `${t}`, vt = ({ className: t, disabled: r, size: e = "md", ...o }) => /* @__PURE__ */ n(
361
+ "button",
362
+ {
363
+ className: a(
364
+ "tw:inline-flex tw:rounded-md tw:focus-ring",
365
+ "tw:text-brand tw:highlight:text-brand-dark tw:highlight:underline",
366
+ {
367
+ "tw:px-1.5 tw:py-1 tw:text-sm": e === "sm",
368
+ "tw:px-3 tw:py-1.5": e === "md",
369
+ "tw:px-4 tw:py-2 tw:text-lg": e === "lg",
370
+ "tw:pointer-events-none tw:opacity-65": r
371
+ },
372
+ t
373
+ ),
374
+ disabled: r,
375
+ ...o
376
+ }
377
+ ), M = [
378
+ "tw:border tw:border-r-0 tw:last:border-r tw:border-lm-border tw:dark:border-dm-border",
379
+ "tw:rounded-none tw:first:rounded-l tw:last:rounded-r"
380
+ ], j = (t = !1) => a(
381
+ M,
382
+ "tw:px-3 py-2 tw:cursor-pointer tw:no-underline",
383
+ "tw:focus-ring tw:focus-visible:z-1",
384
+ {
385
+ "tw:highlight:bg-lm-secondary tw:dark:highlight:bg-dm-secondary tw:text-brand": !t,
386
+ "tw:bg-lm-brand tw:dark:bg-dm-brand tw:text-white": t
387
+ }
388
+ ), k = ({ children: t }) => /* @__PURE__ */ n("span", { "aria-hidden": !0, className: a(M, "tw:px-3 py-2 tw:text-gray-400"), children: t }), H = () => /* @__PURE__ */ n(k, { children: y });
389
+ function et({ children: t, active: r, isEllipsis: e, href: o, ...d }) {
390
+ const l = v(() => j(r), [r]);
391
+ return e ? /* @__PURE__ */ n(H, {}) : /* @__PURE__ */ n(R, { className: l, to: o, ...d, children: t });
392
+ }
393
+ function ot({ children: t, active: r, isEllipsis: e, ...o }) {
394
+ const d = v(() => j(r), [r]);
395
+ return e ? /* @__PURE__ */ n(H, {}) : /* @__PURE__ */ n("button", { type: "button", className: d, ...o, children: t });
396
+ }
397
+ const Nt = ({ currentPage: t, pagesCount: r, ...e }) => {
398
+ const o = "urlForPage" in e, d = o ? et : ot, l = f(
399
+ (s) => o ? { href: u(s) ? void 0 : e.urlForPage(s) } : { onClick: () => !u(s) && e.onPageChange(s) },
400
+ [o, e]
401
+ );
402
+ return r < 2 ? null : /* @__PURE__ */ c("div", { className: "tw:select-none tw:flex", "data-testid": "paginator", children: [
403
+ t === 1 ? /* @__PURE__ */ n(k, { children: /* @__PURE__ */ n(b, { size: "xs", icon: T }) }) : /* @__PURE__ */ n(d, { ...l(Math.max(1, t - 1)), "aria-label": "Previous", children: /* @__PURE__ */ n(b, { size: "xs", icon: T }) }),
404
+ Z(t, r).map((s, i) => /* @__PURE__ */ n(
405
+ d,
406
+ {
407
+ active: s === t,
408
+ isEllipsis: u(s),
409
+ ...l(s),
410
+ children: tt(s)
411
+ },
412
+ rt(s, i)
413
+ )),
414
+ t === r ? /* @__PURE__ */ n(k, { children: /* @__PURE__ */ n(b, { size: "xs", icon: I }) }) : /* @__PURE__ */ n(d, { ...l(Math.min(r, t + 1)), "aria-label": "Next", children: /* @__PURE__ */ n(b, { size: "xs", icon: I }) })
415
+ ] });
416
+ }, nt = ({ className: t, ...r }) => /* @__PURE__ */ n(
417
+ "div",
418
+ {
419
+ className: a(
420
+ "tw:px-4 tw:py-3 tw:rounded-t-md",
421
+ "tw:bg-lm-primary tw:dark:bg-dm-primary tw:border-b tw:border-lm-border tw:dark:border-dm-border",
422
+ t
423
+ ),
424
+ ...r
425
+ }
426
+ ), dt = ({ className: t, ...r }) => /* @__PURE__ */ n(
427
+ "div",
428
+ {
429
+ className: a(
430
+ "tw:p-4 tw:bg-lm-primary tw:dark:bg-dm-primary tw:first:rounded-t-md",
431
+ "tw:first:rounded-t-md tw:last:rounded-b-md",
432
+ t
433
+ ),
434
+ ...r
435
+ }
436
+ ), lt = ({ className: t, ...r }) => /* @__PURE__ */ n(
437
+ "div",
438
+ {
439
+ className: a(
440
+ "tw:px-4 tw:py-3 tw:rounded-b-md",
441
+ "tw:bg-lm-primary tw:dark:bg-dm-primary tw:border-t tw:border-lm-border tw:dark:border-dm-border",
442
+ t
443
+ ),
444
+ ...r
445
+ }
446
+ ), st = ({ className: t, ...r }) => /* @__PURE__ */ n(
447
+ "div",
448
+ {
449
+ className: a(
450
+ "tw:group/card tw:rounded-md tw:shadow-md",
451
+ "tw:border tw:border-lm-border tw:dark:border-dm-border tw:bg-lm-primary tw:dark:bg-dm-primary",
452
+ t
453
+ ),
454
+ ...r
455
+ }
456
+ ), x = Object.assign(st, { Body: dt, Header: nt, Footer: lt }), Tt = ({ bodyClassName: t, children: r, ...e }) => {
457
+ const { title: o, titleSize: d = "md", ...l } = "title" in e ? e : {
458
+ ...e,
459
+ title: void 0,
460
+ titleSize: void 0
461
+ };
462
+ return /* @__PURE__ */ c(x, { ...l, children: [
463
+ o && /* @__PURE__ */ c(x.Header, { children: [
464
+ d === "lg" && /* @__PURE__ */ n("h4", { children: o }),
465
+ d === "md" && /* @__PURE__ */ n("h5", { children: o }),
466
+ d === "sm" && /* @__PURE__ */ n("h6", { children: o })
467
+ ] }),
468
+ /* @__PURE__ */ n(x.Body, { className: t, children: r })
469
+ ] });
470
+ };
471
+ export {
472
+ ut as Button,
473
+ x as Card,
474
+ pt as CloseButton,
475
+ y as ELLIPSIS,
476
+ F as Input,
477
+ E as Label,
478
+ ft as LabelledInput,
479
+ yt as LabelledSelect,
480
+ vt as LinkButton,
481
+ ht as ModalDialog,
482
+ Nt as Paginator,
483
+ xt as SearchInput,
484
+ K as Select,
485
+ Tt as SimpleCard,
486
+ bt as Table,
487
+ X as formatNumber,
488
+ rt as keyForPage,
489
+ u as pageIsEllipsis,
490
+ tt as prettifyPageNumber,
491
+ Z as progressivePagination,
492
+ kt as roundTen,
493
+ Q as useTimeout
494
+ };
@@ -0,0 +1,153 @@
1
+ /*
2
+ * Override `dark:` variant, which applies based on `prefers-color-scheme` by default, and make it apply based on the
3
+ * presence of `data-theme="dark"` attribute instead.
4
+ */
5
+ @custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
6
+
7
+ @theme static inline {
8
+ /* Light mode */
9
+ /*--color-lm-main: #2078CF;*/ /*Properly accessible with white background*/
10
+ --color-lm-main: #4696e5; /* TODO Rename to "brand" */
11
+ --color-lm-main-dark: #1f69c0;
12
+ --color-lm-primary: #ffffff;
13
+ --color-lm-primary-alfa: rgb(var(--tw-color-lm-primary) / .5);
14
+ --color-lm-secondary: #f5f6fe;
15
+ --color-lm-text: #232323;
16
+ --color-lm-border: rgb(0 0 0 / .125);
17
+ --color-lm-table-border: #dee2e6;
18
+ --color-lm-active: #eeeeee;
19
+ --color-lm-brand: var(--tw-color-lm-main); /* TODO Rename to "main" */
20
+ --color-lm-input: var(--tw-color-lm-primary);
21
+ --color-lm-disabled-input: var(--tw-color-lm-secondary);
22
+ --color-lm-input-text: #495057;
23
+ --color-lm-input-border: rgb(0 0 0 / .19);
24
+ --color-lm-table-highlight: rgb(0 0 0 / .075);
25
+
26
+ /* Dark mode */
27
+ --color-dm-main: #4696e5; /* TODO Rename to "brand" */
28
+ --color-dm-main-dark: #1f69c0;
29
+ --color-dm-primary: #161b22;
30
+ --color-dm-primary-alfa: rgb(var(--tw-color-dm-primary) / .8);
31
+ --color-dm-secondary: #0f131a;
32
+ --color-dm-text: rgb(201 209 217);
33
+ --color-dm-border: rgb(255 255 255 / .15);
34
+ --color-dm-table-border: #393d43;
35
+ --color-dm-active: var(--tw-color-dm-secondary);
36
+ --color-dm-brand: #0b2d4e; /* TODO Rename to "main" */
37
+ --color-dm-input: rgb(17.9928571429 22.0821428571 27.8071428571);
38
+ --color-dm-disabled-input: rgb(26.0071428571 31.9178571429 40.1928571429);
39
+ --color-dm-input-text: var(--tw-color-dm-text);
40
+ --color-dm-input-border: var(--tw-color-dm-border);
41
+ --color-dm-table-highlight: var(--tw-color-dm-border);
42
+
43
+ /* TODO Remove these two colors */
44
+ --color-brand: #4696e5;
45
+ --color-brand-dark: #1f69c0;
46
+
47
+ /* General color palette */
48
+ --color-danger: #dc3545;
49
+ --color-danger-dark: #bb2d3b;
50
+ --color-warning: #ffc107;
51
+ --color-warning-dark: #ffca2c;
52
+ --color-placeholder: #6c757d;
53
+ }
54
+
55
+ @layer base {
56
+ html:not([data-theme='dark']) {
57
+ --primary-color: var(--tw-color-lm-primary);
58
+ --primary-color-alfa: var(--tw-color-lm-primary-alfa);
59
+ --secondary-color: var(--tw-color-lm-secondary);
60
+ --text-color: var(--tw-color-lm-text);
61
+ --border-color: var(--tw-color-lm-border);
62
+ --active-color: var(--tw-color-lm-active);
63
+ --brand-color: var(--tw-color-lm-brand);
64
+ --input-color: var(--tw-color-lm-input);
65
+ --input-disabled-color: var(--tw-color-lm-disabled-input);
66
+ --input-text-color: var(--tw-color-lm-input-text);
67
+ --input-border-color: var(--tw-color-lm-input-border);
68
+ --table-border-color: var(--tw-color-lm-table-border);
69
+ --table-highlight-color: var(--tw-color-lm-table-highlight);
70
+ }
71
+
72
+ html[data-theme='dark'] {
73
+ --primary-color: var(--tw-color-dm-primary);
74
+ --primary-color-alfa: var(--tw-color-dm-primary-alfa);
75
+ --secondary-color: var(--tw-color-dm-secondary);
76
+ --text-color: var(--tw-color-dm-text);
77
+ --border-color: var(--tw-color-dm-border);
78
+ --active-color: var(--tw-color-dm-active);
79
+ --brand-color: var(--tw-color-dm-brand);
80
+ --input-color: var(--tw-color-dm-input);
81
+ --input-disabled-color: var(--tw-color-dm-disabled-input);
82
+ --input-text-color: var(--tw-color-dm-input-text);
83
+ --input-border-color: var(--tw-color-dm-input-border);
84
+ --table-border-color: var(--tw-color-dm-table-border);
85
+ --table-highlight-color: var(--tw-color-dm-table-highlight);
86
+ }
87
+ }
88
+
89
+ @layer base {
90
+ :root {
91
+ --header-height: 56px;
92
+ @apply tw:scheme-normal tw:dark:scheme-dark tw:scroll-auto;
93
+ }
94
+
95
+ html, body {
96
+ @apply tw:h-full tw:bg-lm-secondary tw:dark:bg-dm-secondary tw:text-lm-text tw:dark:text-dm-text;
97
+ }
98
+
99
+ a {
100
+ /* Set these styles as plain CSS instead of @apply to avoid higher specificity */
101
+ /* TODO Use `main` color, not `brand` color */
102
+ color: var(--tw-color-brand);
103
+ border-radius: var(--tw-radius-xs);
104
+
105
+ @apply tw:focus-visible:outline-3 tw:focus-visible:outline-brand/50 tw:focus-visible:outline-offset-3 tw:focus-visible:z-1;
106
+ }
107
+
108
+ h1 {
109
+ @apply tw:text-[2.5rem]/[1.2] tw:m-0 tw:font-medium;
110
+ }
111
+ h2 {
112
+ @apply tw:text-[2rem]/[1.2] tw:m-0 tw:font-medium;
113
+ }
114
+ h3 {
115
+ @apply tw:text-[1.75rem]/[1.2] tw:m-0 tw:font-medium;
116
+ }
117
+ h4 {
118
+ @apply tw:text-2xl/[1.2] tw:m-0 tw:font-medium;
119
+ }
120
+ h5 {
121
+ @apply tw:text-xl/[1.2] tw:m-0 tw:font-medium;
122
+ }
123
+ h6 {
124
+ @apply tw:text-base/[1.2] tw:m-0 tw:font-medium;
125
+ }
126
+
127
+ hr {
128
+ @apply tw:my-3
129
+ }
130
+
131
+ p {
132
+ @apply tw:m-0;
133
+ }
134
+ }
135
+
136
+ @utility focus-ring-base {
137
+ @apply tw:outline-none tw:focus-visible:ring-3 tw:focus-visible:z-1 tw:transition-[box-shadow];
138
+ }
139
+
140
+ @utility focus-ring {
141
+ @apply tw:focus-ring-base tw:focus-visible:ring-brand/50;
142
+ }
143
+
144
+ @utility focus-ring-danger {
145
+ @apply tw:focus-ring-base tw:focus-visible:ring-danger/50;
146
+ }
147
+
148
+ @custom-variant highlight {
149
+ &:hover,
150
+ &:focus {
151
+ @slot;
152
+ }
153
+ }
package/package.json CHANGED
@@ -7,19 +7,30 @@
7
7
  "type": "module",
8
8
  "main": "./dist/index.js",
9
9
  "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ },
15
+ "./tailwind": {
16
+ "import": "./dist/tailwind.js",
17
+ "types": "./dist/tailwind.d.ts"
18
+ },
19
+ "./tailwind.preset.css": {
20
+ "style": "./dist/tailwind.preset.css",
21
+ "default": "./dist/tailwind.preset.css"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
10
25
  "files": [
11
26
  "dist"
12
27
  ],
13
28
  "scripts": {
14
- "build": "vite build && cp ./src/base.scss ./dist/base.scss",
29
+ "build": "vite build && cp ./src/base.scss ./dist/base.scss && cp ./src/tailwind/tailwind.preset.css ./dist/tailwind.preset.css",
15
30
  "test": "vitest run",
16
31
  "test:ci": "npm run test -- --coverage",
17
- "lint": "npm run lint:css && npm run lint:js",
18
- "lint:css": "stylelint src/*.scss src/**/*.scss dev/*.scss dev/**/*.scss",
19
- "lint:js": "eslint dev src test",
20
- "lint:fix": "npm run lint:css:fix && npm run lint:js:fix",
21
- "lint:css:fix": "npm run lint:css -- --fix",
22
- "lint:js:fix": "npm run lint:js -- --fix",
32
+ "lint": "eslint dev src test",
33
+ "lint:fix": "npm run lint:js -- --fix",
23
34
  "types": "tsc",
24
35
  "dev": "vite serve --host=0.0.0.0 --port 3001"
25
36
  },
@@ -33,37 +44,42 @@
33
44
  "react": "^18.3 || ^19.0",
34
45
  "react-dom": "^18.3 || ^19.0",
35
46
  "react-router": "^7.0.2",
36
- "reactstrap": "^9.2.0"
47
+ "reactstrap": "^9.2.0",
48
+ "tailwindcss": "^4.0.1"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "tailwindcss": {
52
+ "optional": true
53
+ }
37
54
  },
38
55
  "devDependencies": {
39
56
  "@shlinkio/eslint-config-js-coding-standard": "~3.4.0",
40
- "@shlinkio/stylelint-config-css-coding-standard": "~1.1.1",
41
- "@stylistic/eslint-plugin": "^4.1.0",
57
+ "@stylistic/eslint-plugin": "^4.2.0",
58
+ "@tailwindcss/vite": "^4.0.16",
42
59
  "@testing-library/jest-dom": "^6.6.3",
43
60
  "@testing-library/react": "^16.2.0",
44
61
  "@testing-library/user-event": "^14.6.1",
45
62
  "@total-typescript/shoehorn": "^0.1.2",
46
- "@types/react": "^19.0.10",
63
+ "@types/react": "^19.0.12",
47
64
  "@types/react-dom": "^19.0.4",
48
65
  "@vitejs/plugin-react": "^4.3.4",
49
- "@vitest/coverage-v8": "^3.0.7",
50
- "axe-core": "^4.10.2",
66
+ "@vitest/coverage-v8": "^3.0.9",
67
+ "axe-core": "^4.10.3",
51
68
  "bootstrap": "5.2.3",
52
- "eslint": "^9.21.0",
69
+ "eslint": "^9.23.0",
53
70
  "eslint-plugin-jsx-a11y": "^6.10.2",
54
71
  "eslint-plugin-react": "^7.37.4",
55
- "eslint-plugin-react-compiler": "^19.0.0-beta-40c6c23-20250301",
72
+ "eslint-plugin-react-compiler": "^19.0.0-beta-714736e-20250131",
56
73
  "eslint-plugin-react-hooks": "^5.2.0",
57
74
  "eslint-plugin-simple-import-sort": "^12.1.1",
58
75
  "history": "^5.3.0",
59
76
  "jsdom": "^26.0.0",
60
77
  "resize-observer-polyfill": "^1.5.1",
61
- "sass": "^1.85.1",
62
- "stylelint": "^15.11.0",
63
- "typescript": "^5.7.3",
64
- "typescript-eslint": "^8.25.0",
65
- "vite": "^6.2.0",
66
- "vite-plugin-dts": "^4.5.1",
78
+ "sass": "^1.86.0",
79
+ "typescript": "^5.8.2",
80
+ "typescript-eslint": "^8.27.0",
81
+ "vite": "^6.2.2",
82
+ "vite-plugin-dts": "^4.5.3",
67
83
  "vitest": "^3.0.2"
68
84
  },
69
85
  "browserslist": [
@@ -72,5 +88,5 @@
72
88
  "not ie <= 11",
73
89
  "not op_mini all"
74
90
  ],
75
- "version": "0.7.3"
91
+ "version": "0.8.1"
76
92
  }