@alexandregme/zljs 1.14.0 → 1.16.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/CHANGELOG.md CHANGED
@@ -8,6 +8,29 @@ A change that needs action in an app has a **Migration** note. When updating, re
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [1.16.0] - 2026-11-02
12
+
13
+ ### Added
14
+
15
+ - `Link` in `@alexandregme/zljs/next`: navigates with `next/link` and takes `children`, `href` and `color`, with the `SemanticColor` values. It is always underlined, inherits the font size around it and has a 44px touch target, so keep at least 20px between stacked links
16
+ - `NotFound` takes `backLabel`. "Go back home" was fixed, so an app in another language couldn't translate it
17
+
18
+ ### Changed
19
+
20
+ - `RootLayout` takes the height of the visible screen, with `min-h-dvh` instead of `min-h-screen`, so it doesn't scroll on a phone
21
+ - `NotFound` is built with `Heading`, `Text` and `Link`: the title goes from `text-2xl` semibold to `text-3xl` bold, the message uses the theme's `muted`, and the link follows the theme and is always underlined. It navigates with `next/link` instead of reloading the page
22
+ - `NotFound` takes the height of the visible screen, with `min-h-dvh` instead of `min-h-screen`, so it doesn't scroll on a phone
23
+
24
+ ### Fixed
25
+
26
+ - The `404` of `NotFound` had 1.47:1 contrast on white with `text-gray-300`. It now uses the theme's `muted`, which is 4.84:1
27
+
28
+ ## [1.15.0] - 2026-11-02
29
+
30
+ ### Added
31
+
32
+ - `Icon` takes `color`, with the same `SemanticColor` values as `Text` and `Heading`. With no `color`, or with `none`, it still inherits the color around it
33
+
11
34
  ## [1.14.0] - 2026-11-02
12
35
 
13
36
  ### Changed
@@ -184,7 +207,9 @@ Replace the shade with the class of the `color` being tested, for example `/(^|\
184
207
 
185
208
  The message of `Error` is `text-zl-danger`. `none` and `ghost` didn't change.
186
209
 
187
- [Unreleased]: https://github.com/alexandregme/zljs/compare/v1.14.0...HEAD
210
+ [Unreleased]: https://github.com/alexandregme/zljs/compare/v1.16.0...HEAD
211
+ [1.16.0]: https://github.com/alexandregme/zljs/compare/v1.15.0...v1.16.0
212
+ [1.15.0]: https://github.com/alexandregme/zljs/compare/v1.14.0...v1.15.0
188
213
  [1.14.0]: https://github.com/alexandregme/zljs/compare/v1.13.0...v1.14.0
189
214
  [1.13.0]: https://github.com/alexandregme/zljs/compare/v1.12.0...v1.13.0
190
215
  [1.12.0]: https://github.com/alexandregme/zljs/compare/v1.11.0...v1.12.0
@@ -0,0 +1,83 @@
1
+ "use client";
2
+
3
+ // core/text/text.styles.ts
4
+ import { tv } from "tailwind-variants";
5
+
6
+ // core/types/colors/colors.const.ts
7
+ var semanticTextColors = {
8
+ primary: "text-zl-primary",
9
+ success: "text-zl-success",
10
+ danger: "text-zl-danger",
11
+ warning: "text-zl-warning",
12
+ info: "text-zl-info",
13
+ muted: "text-zl-muted",
14
+ none: ""
15
+ };
16
+
17
+ // core/text/text.styles.ts
18
+ var text = tv({
19
+ variants: {
20
+ variant: {
21
+ body: "text-base",
22
+ caption: "text-sm",
23
+ display: "text-3xl font-bold"
24
+ },
25
+ color: semanticTextColors
26
+ },
27
+ defaultVariants: {
28
+ variant: "body"
29
+ }
30
+ });
31
+
32
+ // core/text/text.tsx
33
+ import { jsx } from "react/jsx-runtime";
34
+ var Text = ({
35
+ children,
36
+ as: Tag = "p",
37
+ variant = "body",
38
+ color
39
+ }) => {
40
+ return /* @__PURE__ */ jsx(Tag, { "data-testid": "text", className: text({ variant, color }), children });
41
+ };
42
+
43
+ // core/heading/heading.const.ts
44
+ var headingTags = {
45
+ 1: "h1",
46
+ 2: "h2",
47
+ 3: "h3",
48
+ 4: "h4",
49
+ 5: "h5",
50
+ 6: "h6"
51
+ };
52
+
53
+ // core/heading/heading.styles.ts
54
+ import { tv as tv2 } from "tailwind-variants";
55
+ var heading = tv2({
56
+ variants: {
57
+ level: {
58
+ 1: "text-3xl font-bold",
59
+ 2: "text-2xl font-bold",
60
+ 3: "text-xl font-semibold",
61
+ 4: "text-lg font-semibold",
62
+ 5: "text-base font-medium",
63
+ 6: "text-sm font-medium"
64
+ },
65
+ color: semanticTextColors
66
+ },
67
+ defaultVariants: {
68
+ level: 1
69
+ }
70
+ });
71
+
72
+ // core/heading/heading.tsx
73
+ import { jsx as jsx2 } from "react/jsx-runtime";
74
+ var Heading = ({ children, level = 1, color }) => {
75
+ const Tag = headingTags[level];
76
+ return /* @__PURE__ */ jsx2(Tag, { className: heading({ level, color }), children });
77
+ };
78
+
79
+ export {
80
+ semanticTextColors,
81
+ Text,
82
+ Heading
83
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Semantic colors for action-oriented components (Button, Heading, etc.)
3
+ */
4
+ type SemanticColor = "primary" | "success" | "danger" | "warning" | "info" | "muted" | "none";
5
+ /**
6
+ * Literal colors for data visualization components (StatsCard, etc.)
7
+ */
8
+ type DataColor = "blue" | "red" | "yellow" | "green" | "gray" | "orange";
9
+
10
+ export type { DataColor as D, SemanticColor as S };
package/dist/index.d.ts CHANGED
@@ -1,20 +1,12 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ChangeEvent, FormEvent, HTMLInputAutoCompleteAttribute, FocusEvent, KeyboardEvent, Ref } from 'react';
3
+ import { S as SemanticColor, D as DataColor } from './colors-CvgrDPVc.js';
3
4
  export { FormInput, FormInputProps, FormProvider, FormProviderProps } from './rhf/index.js';
4
5
  export { toast } from 'sonner';
5
6
  import * as zustand from 'zustand';
6
7
  import { Store, Reducer, Middleware, Dispatch } from '@reduxjs/toolkit';
7
8
  import 'react-hook-form';
8
9
 
9
- /**
10
- * Semantic colors for action-oriented components (Button, Heading, etc.)
11
- */
12
- type SemanticColor = "primary" | "success" | "danger" | "warning" | "info" | "muted" | "none";
13
- /**
14
- * Literal colors for data visualization components (StatsCard, etc.)
15
- */
16
- type DataColor = "blue" | "red" | "yellow" | "green" | "gray" | "orange";
17
-
18
10
  /**
19
11
  * Sizes shared by every sized component (Icon, Button, Input, etc.)
20
12
  */
@@ -26,6 +18,7 @@ type IconName = (typeof iconNames)[number];
26
18
  interface IconProps {
27
19
  name: IconName;
28
20
  size?: Size;
21
+ color?: SemanticColor;
29
22
  "aria-label"?: string;
30
23
  }
31
24
 
@@ -195,7 +188,7 @@ type TextAreaProps = TextAreaName & {
195
188
 
196
189
  declare const TextArea: ({ label: labelText, "aria-label": ariaLabel, name, placeholder, autoComplete, value, rows, error, disabled, required, onChange, onBlur, onKeyDown, ref, }: TextAreaProps) => react.JSX.Element;
197
190
 
198
- declare const Icon: ({ name, size, "aria-label": ariaLabel, }: IconProps) => react.JSX.Element;
191
+ declare const Icon: ({ name, size, color, "aria-label": ariaLabel, }: IconProps) => react.JSX.Element;
199
192
 
200
193
  interface StatsCardProps {
201
194
  label: string;
@@ -505,5 +498,5 @@ declare class HTTPClient {
505
498
  patch<T = unknown>(url: string, config?: HttpRequestConfig): Promise<T | undefined>;
506
499
  }
507
500
 
508
- export { AppWrapper, Button, Checkbox, ConnectedModal, DataGrid, DocumentWrapper, Error, Form, GlobalState, HTTPClient, Heading, Icon, Input, Loader, Modal, PageWrapper, Radio, Select, Sidebar, Spinner, StatsCard, Tag, Text, TextArea, Toaster, WhatsAppButton, splitURLasPath, useAppStore, useModalStore, usePageStore, useRouterStore, useToastStore };
509
- export type { AppState, AppWrapperProps, ButtonColor, ButtonProps, ButtonVariant, CheckboxProps, CheckboxVariant, ConnectedModalProps, DataColor, DataGridColumn, DataGridProps, DocumentWrapperProps, ErrorProps, ExtendedStore, FormProps, GlobalStateProps, HeadingProps, HttpClientConfig, HttpRequestConfig, IconName, IconProps, InitPageFunction, InputProps, LoaderProps, ModalEntry, ModalProps, ModalState, PageState, PageWrapperProps, RadioProps, RouterState, SelectOption, SelectProps, SemanticColor, SidebarItem, SidebarProps, SidebarSection, SidebarUser, Size, SpinnerProps, SpinnerVariant, StatsCardProps, TagProps, TextAreaProps, TextProps, TextVariant, Toast, ToastState, ToastType, WhatsAppButtonProps };
501
+ export { AppWrapper, Button, Checkbox, ConnectedModal, DataColor, DataGrid, DocumentWrapper, Error, Form, GlobalState, HTTPClient, Heading, Icon, Input, Loader, Modal, PageWrapper, Radio, Select, SemanticColor, Sidebar, Spinner, StatsCard, Tag, Text, TextArea, Toaster, WhatsAppButton, splitURLasPath, useAppStore, useModalStore, usePageStore, useRouterStore, useToastStore };
502
+ export type { AppState, AppWrapperProps, ButtonColor, ButtonProps, ButtonVariant, CheckboxProps, CheckboxVariant, ConnectedModalProps, DataGridColumn, DataGridProps, DocumentWrapperProps, ErrorProps, ExtendedStore, FormProps, GlobalStateProps, HeadingProps, HttpClientConfig, HttpRequestConfig, IconName, IconProps, InitPageFunction, InputProps, LoaderProps, ModalEntry, ModalProps, ModalState, PageState, PageWrapperProps, RadioProps, RouterState, SelectOption, SelectProps, SidebarItem, SidebarProps, SidebarSection, SidebarUser, Size, SpinnerProps, SpinnerVariant, StatsCardProps, TagProps, TextAreaProps, TextProps, TextVariant, Toast, ToastState, ToastType, WhatsAppButtonProps };
package/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
1
  "use client";
2
+ import {
3
+ Heading,
4
+ Text,
5
+ semanticTextColors
6
+ } from "./chunk-CXA5QIHP.js";
2
7
  import {
3
8
  Error,
4
9
  FormInput,
@@ -23,11 +28,20 @@ var iconSizes = {
23
28
  lg: 24
24
29
  };
25
30
 
31
+ // core/icon/icon.styles.ts
32
+ import { tv } from "tailwind-variants";
33
+ var icon = tv({
34
+ variants: {
35
+ color: semanticTextColors
36
+ }
37
+ });
38
+
26
39
  // core/icon/icon.tsx
27
40
  import { Fragment, jsx } from "react/jsx-runtime";
28
41
  var Icon = ({
29
42
  name,
30
43
  size = "md",
44
+ color,
31
45
  "aria-label": ariaLabel
32
46
  }) => {
33
47
  const isBrand = name.startsWith("Si");
@@ -37,6 +51,7 @@ var Icon = ({
37
51
  "span",
38
52
  {
39
53
  "data-testid": name,
54
+ className: icon({ color }),
40
55
  role: ariaLabel ? "img" : void 0,
41
56
  "aria-label": ariaLabel,
42
57
  children: /* @__PURE__ */ jsx(IconComponent, { size: iconSizes[size], "aria-hidden": "true" })
@@ -45,8 +60,8 @@ var Icon = ({
45
60
  };
46
61
 
47
62
  // core/button/button.styles.ts
48
- import { tv } from "tailwind-variants";
49
- var button = tv({
63
+ import { tv as tv2 } from "tailwind-variants";
64
+ var button = tv2({
50
65
  // outline-current covers the colors with no outline class (ghost, none): without it, browsers keep their default focus color.
51
66
  base: "cursor-pointer font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center justify-center",
52
67
  variants: {
@@ -157,19 +172,19 @@ var Button = ({
157
172
  color = "primary",
158
173
  size = "md",
159
174
  fullWidth = false,
160
- icon,
175
+ icon: icon2,
161
176
  iconPosition = "left",
162
177
  disabled = false,
163
178
  "aria-label": ariaLabel,
164
179
  onClick
165
180
  }) => {
166
- const isIconOnly = icon && !children;
181
+ const isIconOnly = icon2 && !children;
167
182
  const variant = variantProp ?? (isIconOnly ? "icon" : "default");
168
183
  const renderContent = () => {
169
- if (!icon) {
184
+ if (!icon2) {
170
185
  return children;
171
186
  }
172
- const iconElement = /* @__PURE__ */ jsx2(Icon, { name: icon, size });
187
+ const iconElement = /* @__PURE__ */ jsx2(Icon, { name: icon2, size });
173
188
  if (isIconOnly) {
174
189
  return iconElement;
175
190
  }
@@ -215,11 +230,11 @@ import { useState } from "react";
215
230
  import { useId } from "react";
216
231
 
217
232
  // core/forms/checkbox/checkbox.styles.ts
218
- import { tv as tv2 } from "tailwind-variants";
219
- var wrapper = tv2({
233
+ import { tv as tv3 } from "tailwind-variants";
234
+ var wrapper = tv3({
220
235
  base: "flex flex-col gap-1"
221
236
  });
222
- var innerWrapper = tv2({
237
+ var innerWrapper = tv3({
223
238
  base: "flex cursor-pointer has-disabled:cursor-not-allowed",
224
239
  variants: {
225
240
  variant: {
@@ -231,7 +246,7 @@ var innerWrapper = tv2({
231
246
  variant: "default"
232
247
  }
233
248
  });
234
- var checkbox = tv2({
249
+ var checkbox = tv3({
235
250
  base: "shrink-0 accent-zl-primary border-gray-300 rounded focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed",
236
251
  variants: {
237
252
  size: {
@@ -244,7 +259,7 @@ var checkbox = tv2({
244
259
  size: "md"
245
260
  }
246
261
  });
247
- var label = tv2({
262
+ var label = tv3({
248
263
  base: "min-w-0 flex-1 text-sm text-gray-700 select-none"
249
264
  });
250
265
 
@@ -285,8 +300,8 @@ var Checkbox = ({
285
300
  };
286
301
 
287
302
  // core/forms/form/form.styles.ts
288
- import { tv as tv3 } from "tailwind-variants";
289
- var fieldset = tv3({
303
+ import { tv as tv4 } from "tailwind-variants";
304
+ var fieldset = tv4({
290
305
  base: "border-none p-0"
291
306
  });
292
307
 
@@ -304,14 +319,14 @@ var Form = ({ children, onSubmit, disabled }) => {
304
319
  import { useId as useId2 } from "react";
305
320
 
306
321
  // core/forms/radio/radio.styles.ts
307
- import { tv as tv4 } from "tailwind-variants";
308
- var wrapper2 = tv4({
322
+ import { tv as tv5 } from "tailwind-variants";
323
+ var wrapper2 = tv5({
309
324
  base: "flex w-fit items-center gap-2 cursor-pointer has-disabled:cursor-not-allowed"
310
325
  });
311
- var radio = tv4({
326
+ var radio = tv5({
312
327
  base: "w-4 h-4 shrink-0 accent-zl-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:cursor-not-allowed"
313
328
  });
314
- var label2 = tv4({
329
+ var label2 = tv5({
315
330
  base: "text-sm text-gray-700 select-none"
316
331
  });
317
332
 
@@ -358,14 +373,14 @@ var Radio = ({
358
373
  import { useId as useId3 } from "react";
359
374
 
360
375
  // core/forms/select/select.styles.ts
361
- import { tv as tv5 } from "tailwind-variants";
362
- var wrapper3 = tv5({
376
+ import { tv as tv6 } from "tailwind-variants";
377
+ var wrapper3 = tv6({
363
378
  base: "flex flex-col gap-1"
364
379
  });
365
- var label3 = tv5({
380
+ var label3 = tv6({
366
381
  base: "text-sm font-medium text-gray-700"
367
382
  });
368
- var select = tv5({
383
+ var select = tv6({
369
384
  base: "px-3 py-2 border border-gray-500 rounded-md text-sm bg-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:bg-gray-100 disabled:cursor-not-allowed"
370
385
  });
371
386
 
@@ -423,14 +438,14 @@ var Select = ({
423
438
  import { useId as useId4 } from "react";
424
439
 
425
440
  // core/forms/text-area/text-area.styles.ts
426
- import { tv as tv6 } from "tailwind-variants";
427
- var wrapper4 = tv6({
441
+ import { tv as tv7 } from "tailwind-variants";
442
+ var wrapper4 = tv7({
428
443
  base: "flex flex-col gap-1"
429
444
  });
430
- var label4 = tv6({
445
+ var label4 = tv7({
431
446
  base: "text-sm font-medium text-gray-700"
432
447
  });
433
- var textarea = tv6({
448
+ var textarea = tv7({
434
449
  base: "px-3 py-2 border border-gray-500 rounded-md text-sm placeholder:text-gray-500 resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:bg-gray-100 disabled:cursor-not-allowed"
435
450
  });
436
451
 
@@ -521,8 +536,8 @@ var DataGridToolbar = ({ gridApi }) => {
521
536
  };
522
537
 
523
538
  // core/tag/tag.styles.ts
524
- import { tv as tv7 } from "tailwind-variants";
525
- var tag = tv7({
539
+ import { tv as tv8 } from "tailwind-variants";
540
+ var tag = tv8({
526
541
  base: "inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium",
527
542
  variants: {
528
543
  color: {
@@ -721,46 +736,6 @@ var DataGrid = ({
721
736
  );
722
737
  };
723
738
 
724
- // core/text/text.styles.ts
725
- import { tv as tv8 } from "tailwind-variants";
726
-
727
- // core/types/colors/colors.const.ts
728
- var semanticTextColors = {
729
- primary: "text-zl-primary",
730
- success: "text-zl-success",
731
- danger: "text-zl-danger",
732
- warning: "text-zl-warning",
733
- info: "text-zl-info",
734
- muted: "text-zl-muted",
735
- none: ""
736
- };
737
-
738
- // core/text/text.styles.ts
739
- var text = tv8({
740
- variants: {
741
- variant: {
742
- body: "text-base",
743
- caption: "text-sm",
744
- display: "text-3xl font-bold"
745
- },
746
- color: semanticTextColors
747
- },
748
- defaultVariants: {
749
- variant: "body"
750
- }
751
- });
752
-
753
- // core/text/text.tsx
754
- import { jsx as jsx12 } from "react/jsx-runtime";
755
- var Text = ({
756
- children,
757
- as: Tag2 = "p",
758
- variant = "body",
759
- color
760
- }) => {
761
- return /* @__PURE__ */ jsx12(Tag2, { "data-testid": "text", className: text({ variant, color }), children });
762
- };
763
-
764
739
  // core/stats-card/stats-card.styles.ts
765
740
  import { tv as tv9 } from "tailwind-variants";
766
741
  var statsCard = tv9({
@@ -778,11 +753,11 @@ var statsCard = tv9({
778
753
  });
779
754
 
780
755
  // core/stats-card/stats-card.tsx
781
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
756
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
782
757
  var StatsCard = ({ label: label5, value, color }) => {
783
758
  return /* @__PURE__ */ jsxs10("div", { "data-testid": "stats-card", className: statsCard({ color }), children: [
784
- /* @__PURE__ */ jsx13(Text, { variant: "caption", children: label5 }),
785
- /* @__PURE__ */ jsx13(Text, { variant: "display", children: value })
759
+ /* @__PURE__ */ jsx12(Text, { variant: "caption", children: label5 }),
760
+ /* @__PURE__ */ jsx12(Text, { variant: "display", children: value })
786
761
  ] });
787
762
  };
788
763
 
@@ -823,7 +798,7 @@ var body = tv10({
823
798
  });
824
799
 
825
800
  // core/modal/modal.tsx
826
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
801
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
827
802
  var Modal = ({
828
803
  open,
829
804
  onClose,
@@ -832,8 +807,8 @@ var Modal = ({
832
807
  children,
833
808
  size = "md"
834
809
  }) => {
835
- return /* @__PURE__ */ jsx14(Dialog.Root, { open, onOpenChange: (isOpen) => !isOpen && onClose(), children: /* @__PURE__ */ jsxs11(Dialog.Portal, { children: [
836
- /* @__PURE__ */ jsx14(Dialog.Overlay, { className: overlay() }),
810
+ return /* @__PURE__ */ jsx13(Dialog.Root, { open, onOpenChange: (isOpen) => !isOpen && onClose(), children: /* @__PURE__ */ jsxs11(Dialog.Portal, { children: [
811
+ /* @__PURE__ */ jsx13(Dialog.Overlay, { className: overlay() }),
837
812
  /* @__PURE__ */ jsxs11(
838
813
  Dialog.Content,
839
814
  {
@@ -841,11 +816,11 @@ var Modal = ({
841
816
  ...!description2 && { "aria-describedby": void 0 },
842
817
  children: [
843
818
  /* @__PURE__ */ jsxs11("div", { className: header(), children: [
844
- /* @__PURE__ */ jsx14(Dialog.Title, { className: title(), children: title2 }),
845
- /* @__PURE__ */ jsx14(Button, { icon: "X", "aria-label": "Close", onClick: onClose })
819
+ /* @__PURE__ */ jsx13(Dialog.Title, { className: title(), children: title2 }),
820
+ /* @__PURE__ */ jsx13(Button, { icon: "X", "aria-label": "Close", onClick: onClose })
846
821
  ] }),
847
- description2 && /* @__PURE__ */ jsx14(Dialog.Description, { className: description(), children: description2 }),
848
- children && /* @__PURE__ */ jsx14("div", { className: body(), children })
822
+ description2 && /* @__PURE__ */ jsx13(Dialog.Description, { className: description(), children: description2 }),
823
+ children && /* @__PURE__ */ jsx13("div", { className: body(), children })
849
824
  ]
850
825
  }
851
826
  )
@@ -858,7 +833,7 @@ var whatsappBusinessUrl = "whatsapp-business://send";
858
833
  var whatsappMobileUserAgent = /Android|iPhone|iPad|iPod/i;
859
834
 
860
835
  // core/whatsapp-button/whatsapp-button.tsx
861
- import { jsx as jsx15 } from "react/jsx-runtime";
836
+ import { jsx as jsx14 } from "react/jsx-runtime";
862
837
  var WhatsAppButton = ({
863
838
  phone,
864
839
  message,
@@ -883,43 +858,7 @@ var WhatsAppButton = ({
883
858
  }
884
859
  onClick?.();
885
860
  };
886
- return /* @__PURE__ */ jsx15(Button, { color: "success", icon: "SiWhatsapp", onClick: handleClick, children });
887
- };
888
-
889
- // core/heading/heading.const.ts
890
- var headingTags = {
891
- 1: "h1",
892
- 2: "h2",
893
- 3: "h3",
894
- 4: "h4",
895
- 5: "h5",
896
- 6: "h6"
897
- };
898
-
899
- // core/heading/heading.styles.ts
900
- import { tv as tv11 } from "tailwind-variants";
901
- var heading = tv11({
902
- variants: {
903
- level: {
904
- 1: "text-3xl font-bold",
905
- 2: "text-2xl font-bold",
906
- 3: "text-xl font-semibold",
907
- 4: "text-lg font-semibold",
908
- 5: "text-base font-medium",
909
- 6: "text-sm font-medium"
910
- },
911
- color: semanticTextColors
912
- },
913
- defaultVariants: {
914
- level: 1
915
- }
916
- });
917
-
918
- // core/heading/heading.tsx
919
- import { jsx as jsx16 } from "react/jsx-runtime";
920
- var Heading = ({ children, level = 1, color }) => {
921
- const Tag2 = headingTags[level];
922
- return /* @__PURE__ */ jsx16(Tag2, { className: heading({ level, color }), children });
861
+ return /* @__PURE__ */ jsx14(Button, { color: "success", icon: "SiWhatsapp", onClick: handleClick, children });
923
862
  };
924
863
 
925
864
  // core/sidebar/sidebar.tsx
@@ -927,8 +866,8 @@ import { useState as useState3 } from "react";
927
866
  import Link from "next/link.js";
928
867
 
929
868
  // core/sidebar/sidebar.styles.ts
930
- import { tv as tv12 } from "tailwind-variants";
931
- var sidebar = tv12({
869
+ import { tv as tv11 } from "tailwind-variants";
870
+ var sidebar = tv11({
932
871
  base: "h-screen bg-neutral-800 text-neutral-300 flex flex-col overflow-hidden transition-all duration-200",
933
872
  variants: {
934
873
  collapsed: {
@@ -940,16 +879,16 @@ var sidebar = tv12({
940
879
  collapsed: false
941
880
  }
942
881
  });
943
- var nav = tv12({
882
+ var nav = tv11({
944
883
  base: "flex-1 min-h-0 overflow-y-auto overflow-x-hidden"
945
884
  });
946
- var header2 = tv12({
885
+ var header2 = tv11({
947
886
  base: "shrink-0 border-b border-neutral-600/50 grid p-2"
948
887
  });
949
- var section = tv12({
888
+ var section = tv11({
950
889
  base: "border-b border-neutral-600/50 last:border-b-0 grid gap-1 p-2"
951
890
  });
952
- var item = tv12({
891
+ var item = tv11({
953
892
  base: "flex w-full items-center rounded-md text-sm whitespace-nowrap transition-colors cursor-pointer p-1",
954
893
  variants: {
955
894
  active: {
@@ -961,10 +900,10 @@ var item = tv12({
961
900
  active: false
962
901
  }
963
902
  });
964
- var itemIcon = tv12({
903
+ var itemIcon = tv11({
965
904
  base: "p-2"
966
905
  });
967
- var itemLabel = tv12({
906
+ var itemLabel = tv11({
968
907
  base: "truncate transition-all duration-200",
969
908
  variants: {
970
909
  collapsed: {
@@ -976,27 +915,27 @@ var itemLabel = tv12({
976
915
  collapsed: false
977
916
  }
978
917
  });
979
- var footer = tv12({
918
+ var footer = tv11({
980
919
  base: "shrink-0 border-t border-neutral-600/50 p-3 flex items-center gap-3 whitespace-nowrap"
981
920
  });
982
- var avatar = tv12({
921
+ var avatar = tv11({
983
922
  base: "shrink-0 w-8 h-8 rounded-full bg-neutral-600 text-white text-xs font-medium flex items-center justify-center"
984
923
  });
985
- var userInfo = tv12({
924
+ var userInfo = tv11({
986
925
  base: "flex flex-col min-w-0"
987
926
  });
988
- var userName = tv12({
927
+ var userName = tv11({
989
928
  base: "text-sm text-white truncate"
990
929
  });
991
- var userEmail = tv12({
930
+ var userEmail = tv11({
992
931
  base: "text-xs text-neutral-400"
993
932
  });
994
- var toggleButton = tv12({
933
+ var toggleButton = tv11({
995
934
  base: "shrink-0 border-t border-neutral-600/50 grid p-2 [&>button]:justify-start [&>button]:overflow-hidden [&>button]:whitespace-nowrap"
996
935
  });
997
936
 
998
937
  // core/sidebar/sidebar.tsx
999
- import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
938
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1000
939
  var getInitials = (name) => {
1001
940
  const parts = name.trim().split(/\s+/);
1002
941
  if (parts.length >= 2) {
@@ -1017,19 +956,19 @@ var Sidebar = ({
1017
956
  const [collapsed, setCollapsed] = useState3(defaultCollapsed);
1018
957
  const initials = getInitials(user.name);
1019
958
  return /* @__PURE__ */ jsxs12("aside", { className: sidebar({ collapsed }), children: [
1020
- homeHref && /* @__PURE__ */ jsx17(
959
+ homeHref && /* @__PURE__ */ jsx15(
1021
960
  Link,
1022
961
  {
1023
962
  href: homeHref,
1024
963
  className: header2(),
1025
964
  "aria-label": homeLabel || "Home",
1026
965
  children: /* @__PURE__ */ jsxs12("span", { className: item({ active: homeHref === activeHref }), children: [
1027
- /* @__PURE__ */ jsx17("span", { className: itemIcon(), children: /* @__PURE__ */ jsx17(Icon, { name: "Home" }) }),
1028
- /* @__PURE__ */ jsx17("span", { className: itemLabel({ collapsed }), children: homeLabel })
966
+ /* @__PURE__ */ jsx15("span", { className: itemIcon(), children: /* @__PURE__ */ jsx15(Icon, { name: "Home" }) }),
967
+ /* @__PURE__ */ jsx15("span", { className: itemLabel({ collapsed }), children: homeLabel })
1029
968
  ] })
1030
969
  }
1031
970
  ),
1032
- /* @__PURE__ */ jsx17("nav", { "aria-label": "Main navigation", className: nav(), children: sections.map((sectionData, i) => /* @__PURE__ */ jsx17("div", { className: section(), children: sectionData.items.map((sidebarItem) => {
971
+ /* @__PURE__ */ jsx15("nav", { "aria-label": "Main navigation", className: nav(), children: sections.map((sectionData, i) => /* @__PURE__ */ jsx15("div", { className: section(), children: sectionData.items.map((sidebarItem) => {
1033
972
  const isActive = sidebarItem.href === activeHref;
1034
973
  return /* @__PURE__ */ jsxs12(
1035
974
  Link,
@@ -1038,20 +977,20 @@ var Sidebar = ({
1038
977
  className: item({ active: isActive }),
1039
978
  ...isActive && { "aria-current": "page" },
1040
979
  children: [
1041
- /* @__PURE__ */ jsx17("span", { className: itemIcon(), children: /* @__PURE__ */ jsx17(Icon, { name: sidebarItem.icon }) }),
1042
- /* @__PURE__ */ jsx17("span", { className: itemLabel({ collapsed }), children: sidebarItem.label })
980
+ /* @__PURE__ */ jsx15("span", { className: itemIcon(), children: /* @__PURE__ */ jsx15(Icon, { name: sidebarItem.icon }) }),
981
+ /* @__PURE__ */ jsx15("span", { className: itemLabel({ collapsed }), children: sidebarItem.label })
1043
982
  ]
1044
983
  },
1045
984
  sidebarItem.href
1046
985
  );
1047
986
  }) }, i)) }),
1048
987
  /* @__PURE__ */ jsxs12("div", { className: footer(), children: [
1049
- /* @__PURE__ */ jsx17("div", { className: avatar(), children: initials }),
988
+ /* @__PURE__ */ jsx15("div", { className: avatar(), children: initials }),
1050
989
  /* @__PURE__ */ jsxs12("div", { className: userInfo(), children: [
1051
- /* @__PURE__ */ jsx17("span", { className: userName(), children: user.name }),
1052
- /* @__PURE__ */ jsx17("span", { className: userEmail(), children: user.email })
990
+ /* @__PURE__ */ jsx15("span", { className: userName(), children: user.name }),
991
+ /* @__PURE__ */ jsx15("span", { className: userEmail(), children: user.email })
1053
992
  ] }),
1054
- /* @__PURE__ */ jsx17(
993
+ /* @__PURE__ */ jsx15(
1055
994
  Button,
1056
995
  {
1057
996
  color: "ghost",
@@ -1061,7 +1000,7 @@ var Sidebar = ({
1061
1000
  }
1062
1001
  )
1063
1002
  ] }),
1064
- /* @__PURE__ */ jsx17("div", { className: toggleButton(), children: /* @__PURE__ */ jsx17(
1003
+ /* @__PURE__ */ jsx15("div", { className: toggleButton(), children: /* @__PURE__ */ jsx15(
1065
1004
  Button,
1066
1005
  {
1067
1006
  color: "ghost",
@@ -1110,7 +1049,7 @@ var useToastStore = create()(
1110
1049
  );
1111
1050
 
1112
1051
  // core/toaster/toaster.tsx
1113
- import { jsx as jsx18 } from "react/jsx-runtime";
1052
+ import { jsx as jsx16 } from "react/jsx-runtime";
1114
1053
  var Toaster = () => {
1115
1054
  const toasts = useToastStore((s) => s.toasts);
1116
1055
  const removeToast = useToastStore((s) => s.removeToast);
@@ -1123,7 +1062,7 @@ var Toaster = () => {
1123
1062
  removeToast(t.id);
1124
1063
  });
1125
1064
  }, [toasts, removeToast]);
1126
- return /* @__PURE__ */ jsx18(
1065
+ return /* @__PURE__ */ jsx16(
1127
1066
  SonnerToaster,
1128
1067
  {
1129
1068
  position: "bottom-right",
@@ -1202,7 +1141,7 @@ import {
1202
1141
  combineReducers,
1203
1142
  configureStore
1204
1143
  } from "@reduxjs/toolkit";
1205
- import { jsx as jsx19 } from "react/jsx-runtime";
1144
+ import { jsx as jsx17 } from "react/jsx-runtime";
1206
1145
  var store;
1207
1146
  var persistentReducers = {};
1208
1147
  var dynamicReducers = {};
@@ -1241,7 +1180,7 @@ var GlobalState = ({
1241
1180
  preloadedState,
1242
1181
  coreReducers
1243
1182
  );
1244
- return /* @__PURE__ */ jsx19(Provider, { store: reduxStore, children });
1183
+ return /* @__PURE__ */ jsx17(Provider, { store: reduxStore, children });
1245
1184
  };
1246
1185
 
1247
1186
  // core/state/router/router.store.ts
@@ -1300,7 +1239,7 @@ var usePageStore = create5()(
1300
1239
  import { useEffect as useEffect2 } from "react";
1301
1240
  import { useDispatch, useStore } from "react-redux";
1302
1241
  import { useRouter } from "next/compat/router.js";
1303
- import { Fragment as Fragment4, jsx as jsx20 } from "react/jsx-runtime";
1242
+ import { Fragment as Fragment4, jsx as jsx18 } from "react/jsx-runtime";
1304
1243
  var PageWrapper = ({
1305
1244
  reducers,
1306
1245
  title: title2,
@@ -1324,11 +1263,11 @@ var PageWrapper = ({
1324
1263
  initPage(dispatch);
1325
1264
  }
1326
1265
  }, []);
1327
- return /* @__PURE__ */ jsx20(Fragment4, { children });
1266
+ return /* @__PURE__ */ jsx18(Fragment4, { children });
1328
1267
  };
1329
1268
 
1330
1269
  // core/connected-modal/connected-modal.tsx
1331
- import { jsx as jsx21 } from "react/jsx-runtime";
1270
+ import { jsx as jsx19 } from "react/jsx-runtime";
1332
1271
  var ConnectedModal = ({
1333
1272
  modalId,
1334
1273
  title: title2,
@@ -1338,7 +1277,7 @@ var ConnectedModal = ({
1338
1277
  }) => {
1339
1278
  const isOpen = useModalStore((s) => s.isOpen(modalId));
1340
1279
  const closeModal = useModalStore((s) => s.closeModal);
1341
- return /* @__PURE__ */ jsx21(
1280
+ return /* @__PURE__ */ jsx19(
1342
1281
  Modal,
1343
1282
  {
1344
1283
  open: isOpen,
@@ -1359,34 +1298,34 @@ var spinnerIcons = {
1359
1298
  };
1360
1299
 
1361
1300
  // core/spinner/spinner.styles.ts
1362
- import { tv as tv13 } from "tailwind-variants";
1363
- var spinner = tv13({
1301
+ import { tv as tv12 } from "tailwind-variants";
1302
+ var spinner = tv12({
1364
1303
  base: "inline-flex animate-spin text-white"
1365
1304
  });
1366
1305
 
1367
1306
  // core/spinner/spinner.tsx
1368
- import { jsx as jsx22 } from "react/jsx-runtime";
1369
- var Spinner = ({ variant = "circle", size = "md" }) => /* @__PURE__ */ jsx22("span", { "data-testid": "spinner", className: spinner(), children: /* @__PURE__ */ jsx22(Icon, { name: spinnerIcons[variant], size }) });
1307
+ import { jsx as jsx20 } from "react/jsx-runtime";
1308
+ var Spinner = ({ variant = "circle", size = "md" }) => /* @__PURE__ */ jsx20("span", { "data-testid": "spinner", className: spinner(), children: /* @__PURE__ */ jsx20(Icon, { name: spinnerIcons[variant], size }) });
1370
1309
 
1371
1310
  // core/loader/loader.styles.ts
1372
- import { tv as tv14 } from "tailwind-variants";
1373
- var overlay2 = tv14({
1311
+ import { tv as tv13 } from "tailwind-variants";
1312
+ var overlay2 = tv13({
1374
1313
  base: "flex fixed justify-center items-center w-full h-full bg-black/20 z-50"
1375
1314
  });
1376
1315
 
1377
1316
  // core/loader/loader.tsx
1378
- import { jsx as jsx23 } from "react/jsx-runtime";
1317
+ import { jsx as jsx21 } from "react/jsx-runtime";
1379
1318
  var Loader = ({ label: label5 = "Loading" }) => {
1380
1319
  const fetching = useAppStore((s) => s.fetching);
1381
1320
  if (!fetching) return null;
1382
- return /* @__PURE__ */ jsx23("div", { role: "status", "aria-label": label5, className: overlay2(), children: /* @__PURE__ */ jsx23(Spinner, { size: "lg" }) });
1321
+ return /* @__PURE__ */ jsx21("div", { role: "status", "aria-label": label5, className: overlay2(), children: /* @__PURE__ */ jsx21(Spinner, { size: "lg" }) });
1383
1322
  };
1384
1323
 
1385
1324
  // core/app-wrapper/app-wrapper.tsx
1386
1325
  import { useEffect as useEffect3 } from "react";
1387
1326
  import { useRouter as useRouter2 } from "next/compat/router.js";
1388
1327
  import { SessionProvider } from "next-auth/react";
1389
- import { jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
1328
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1390
1329
  var AppWrapper = ({
1391
1330
  children,
1392
1331
  reducers,
@@ -1400,15 +1339,15 @@ var AppWrapper = ({
1400
1339
  setRoute(router.asPath);
1401
1340
  }
1402
1341
  }, [router?.isReady, router?.asPath, setRoute]);
1403
- return /* @__PURE__ */ jsx24(SessionProvider, { children: /* @__PURE__ */ jsxs13(
1342
+ return /* @__PURE__ */ jsx22(SessionProvider, { children: /* @__PURE__ */ jsxs13(
1404
1343
  GlobalState,
1405
1344
  {
1406
1345
  reducers,
1407
1346
  middlewares,
1408
1347
  coreReducers,
1409
1348
  children: [
1410
- /* @__PURE__ */ jsx24(Loader, {}),
1411
- /* @__PURE__ */ jsx24(Toaster, {}),
1349
+ /* @__PURE__ */ jsx22(Loader, {}),
1350
+ /* @__PURE__ */ jsx22(Toaster, {}),
1412
1351
  children
1413
1352
  ]
1414
1353
  }
@@ -1416,13 +1355,13 @@ var AppWrapper = ({
1416
1355
  };
1417
1356
 
1418
1357
  // core/document-wrapper/document-wrapper.tsx
1419
- import { jsx as jsx25 } from "react/jsx-runtime";
1358
+ import { jsx as jsx23 } from "react/jsx-runtime";
1420
1359
  var DocumentWrapper = ({
1421
1360
  children,
1422
1361
  lang = "pt-BR",
1423
1362
  className
1424
1363
  }) => {
1425
- return /* @__PURE__ */ jsx25("html", { lang, children: /* @__PURE__ */ jsx25("body", { className, children }) });
1364
+ return /* @__PURE__ */ jsx23("html", { lang, children: /* @__PURE__ */ jsx23("body", { className, children }) });
1426
1365
  };
1427
1366
 
1428
1367
  // core/http/http.client.ts
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
+ import { SemanticColor } from '../index.js';
3
4
 
4
5
  interface RootLayoutProps {
5
6
  children: ReactNode;
@@ -11,9 +12,18 @@ interface NotFoundProps {
11
12
  title?: string;
12
13
  message?: string;
13
14
  backHref?: string;
15
+ backLabel?: string;
14
16
  }
15
17
 
16
- declare const NotFound: ({ title: notFoundTitle, message: notFoundMessage, backHref, }: NotFoundProps) => react.JSX.Element;
18
+ declare const NotFound: ({ title, message, backHref, backLabel, }: NotFoundProps) => react.JSX.Element;
17
19
 
18
- export { NotFound, RootLayout };
19
- export type { NotFoundProps, RootLayoutProps };
20
+ interface LinkProps {
21
+ children: ReactNode;
22
+ href: string;
23
+ color?: SemanticColor;
24
+ }
25
+
26
+ declare const Link: ({ children, href, color }: LinkProps) => react.JSX.Element;
27
+
28
+ export { Link, NotFound, RootLayout };
29
+ export type { LinkProps, NotFoundProps, RootLayoutProps };
@@ -1,50 +1,81 @@
1
1
  "use client";
2
+ import {
3
+ Heading,
4
+ Text
5
+ } from "../chunk-CXA5QIHP.js";
2
6
 
3
7
  // core/next/root-layout/root-layout.styles.ts
4
8
  import { tv } from "tailwind-variants";
5
- var body = tv({
6
- base: "min-h-screen"
9
+ var rootLayout = tv({
10
+ base: "min-h-dvh"
7
11
  });
8
12
 
9
13
  // core/next/root-layout/root-layout.tsx
10
14
  import { jsx } from "react/jsx-runtime";
11
15
  var RootLayout = ({ children }) => {
12
- return /* @__PURE__ */ jsx("main", { className: body(), children });
16
+ return /* @__PURE__ */ jsx("main", { className: rootLayout(), children });
13
17
  };
14
18
 
15
- // core/next/not-found/not-found.styles.ts
19
+ // core/next/link/link.tsx
20
+ import NextLink from "next/link.js";
21
+
22
+ // core/next/link/link.styles.ts
16
23
  import { tv as tv2 } from "tailwind-variants";
17
- var container = tv2({
18
- base: "flex flex-col items-center justify-center min-h-screen p-4"
19
- });
20
- var code = tv2({
21
- base: "text-6xl font-bold text-gray-300 mb-4"
22
- });
23
- var title = tv2({
24
- base: "text-2xl font-semibold text-gray-900 mb-2"
25
- });
26
- var message = tv2({
27
- base: "text-gray-600 mb-6 text-center max-w-md"
28
- });
29
24
  var link = tv2({
30
- base: "text-blue-600 hover:text-blue-700 hover:underline"
25
+ // outline-current draws the focus in the text color. The invisible ::after grows the touch target to 44px without changing the layout.
26
+ base: "relative rounded-sm underline underline-offset-2 hover:decoration-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current after:absolute after:top-1/2 after:left-1/2 after:h-11 after:w-full after:min-w-11 after:-translate-x-1/2 after:-translate-y-1/2",
27
+ variants: {
28
+ color: {
29
+ primary: "text-zl-primary hover:text-zl-primary-strong",
30
+ success: "text-zl-success hover:text-zl-success-strong",
31
+ danger: "text-zl-danger hover:text-zl-danger-strong",
32
+ warning: "text-zl-warning hover:text-zl-warning-strong",
33
+ info: "text-zl-info hover:text-zl-info-strong",
34
+ muted: "text-zl-muted hover:text-zl-muted-strong",
35
+ none: ""
36
+ }
37
+ },
38
+ defaultVariants: {
39
+ color: "primary"
40
+ }
41
+ });
42
+
43
+ // core/next/link/link.tsx
44
+ import { jsx as jsx2 } from "react/jsx-runtime";
45
+ var Link = ({ children, href, color = "primary" }) => {
46
+ return /* @__PURE__ */ jsx2(NextLink, { href, className: link({ color }), children });
47
+ };
48
+
49
+ // core/next/not-found/not-found.styles.ts
50
+ import { tv as tv3 } from "tailwind-variants";
51
+ var notFound = tv3({
52
+ slots: {
53
+ base: "flex flex-col items-center justify-center gap-6 min-h-dvh p-4 text-center",
54
+ code: "text-6xl font-bold text-zl-muted",
55
+ content: "flex flex-col gap-2 max-w-md"
56
+ }
31
57
  });
32
58
 
33
59
  // core/next/not-found/not-found.tsx
34
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
60
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
35
61
  var NotFound = ({
36
- title: notFoundTitle = "Page not found",
37
- message: notFoundMessage = "The page you are looking for does not exist.",
38
- backHref = "/"
62
+ title = "Page not found",
63
+ message = "The page you are looking for does not exist.",
64
+ backHref = "/",
65
+ backLabel = "Go back home"
39
66
  }) => {
40
- return /* @__PURE__ */ jsxs("div", { className: container(), children: [
41
- /* @__PURE__ */ jsx2("span", { className: code(), children: "404" }),
42
- /* @__PURE__ */ jsx2("h1", { className: title(), children: notFoundTitle }),
43
- /* @__PURE__ */ jsx2("p", { className: message(), children: notFoundMessage }),
44
- /* @__PURE__ */ jsx2("a", { href: backHref, className: link(), children: "Go back home" })
67
+ const { base, code, content } = notFound();
68
+ return /* @__PURE__ */ jsxs("div", { className: base(), children: [
69
+ /* @__PURE__ */ jsx3("span", { className: code(), children: "404" }),
70
+ /* @__PURE__ */ jsxs("div", { className: content(), children: [
71
+ /* @__PURE__ */ jsx3(Heading, { children: title }),
72
+ /* @__PURE__ */ jsx3(Text, { color: "muted", children: message })
73
+ ] }),
74
+ /* @__PURE__ */ jsx3(Link, { href: backHref, children: backLabel })
45
75
  ] });
46
76
  };
47
77
  export {
78
+ Link,
48
79
  NotFound,
49
80
  RootLayout
50
81
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexandregme/zljs",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "Opinionated, accessible React component library for enterprise apps (Next.js, Tailwind CSS v4).",
5
5
  "keywords": [
6
6
  "react",