@stamcat/craftsman 0.0.27-alpha.11 → 0.0.27-alpha.13

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/AGENTS.md CHANGED
@@ -746,6 +746,11 @@ Sass usage (framework source):
746
746
  2. Keep accessibility props in place (`aria-label`, `disabled`, semantic `type`).
747
747
  3. Use `variant="primary"` for main actions and `variant="text"` for low-emphasis actions.
748
748
  4. For loading states, pair `Loader` with accessible status text where needed.
749
+ 5. **Functional component declaration order** — always organize the body in this sequence:
750
+ 1. Hook calls (`useSomething`)
751
+ 2. State (`useState`)
752
+ 3. Derived state / variables (values computed from state or props)
753
+ 4. Action handlers (`const handle*`, `const dispatch*`, `useEffect`)
749
754
 
750
755
  ## Known Limitations (Current Package State)
751
756
 
package/Components.esm.js CHANGED
@@ -11,9 +11,12 @@ import { Modal as l } from "./src/components/Modal/Modal.esm.js";
11
11
  import { Select as u } from "./src/components/Select/Select.esm.js";
12
12
  import { DatePicker as d } from "./src/components/Input/DatePicker.esm.js";
13
13
  import { InputPhone as f } from "./src/components/Input/InputPhone.esm.js";
14
+ import { Pagination as p } from "./src/components/Pagination/Pagination2.esm.js";
15
+ import { Carousel as m } from "./src/components/Carousel/Carousel2.esm.js";
14
16
  //#region src/components/index.ts
15
- var p = /* @__PURE__ */ e({
17
+ var h = /* @__PURE__ */ e({
16
18
  Button: () => t,
19
+ Carousel: () => m,
17
20
  Checkbox: () => o,
18
21
  DatePicker: () => d,
19
22
  Input: () => n,
@@ -21,10 +24,11 @@ var p = /* @__PURE__ */ e({
21
24
  InputPhone: () => f,
22
25
  Loader: () => i,
23
26
  Modal: () => l,
27
+ Pagination: () => p,
24
28
  RadioButton: () => a,
25
29
  Select: () => u,
26
30
  Text: () => c,
27
31
  Textarea: () => s
28
32
  });
29
33
  //#endregion
30
- export { t as Button, o as Checkbox, d as DatePicker, n as Input, r as InputPassword, f as InputPhone, i as Loader, l as Modal, a as RadioButton, u as Select, c as Text, s as Textarea, p as components_exports };
34
+ export { t as Button, m as Carousel, o as Checkbox, d as DatePicker, n as Input, r as InputPassword, f as InputPhone, i as Loader, l as Modal, p as Pagination, a as RadioButton, u as Select, c as Text, s as Textarea, h as components_exports };
package/Utilities.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- import { isEmpty as e, validateEmail as t } from "./src/utilities/validations.esm.js";
2
- export { e as isEmpty, t as validateEmail };
1
+ import { getAmPmLabels as e, getUnitLabel as t, is24HourFormat as n, isEmpty as r, validateEmail as i } from "./src/utilities/validations.esm.js";
2
+ export { e as getAmPmLabels, t as getUnitLabel, n as is24HourFormat, r as isEmpty, i as validateEmail };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamcat/craftsman",
3
- "version": "0.0.27-alpha.11",
3
+ "version": "0.0.27-alpha.13",
4
4
  "type": "module",
5
5
  "description": "A powerful, lightweight framework for design systems",
6
6
  "repository": {
@@ -23,6 +23,7 @@
23
23
  "prepublishOnly": "node ./scripts/prepublishCheck.js"
24
24
  },
25
25
  "dependencies": {
26
+ "@floating-ui/react-dom": "2.1.9",
26
27
  "clsx": "2.1.1",
27
28
  "dompurify": "3.4.13",
28
29
  "embla-carousel-react": "8.6.0",
@@ -33,6 +34,7 @@
33
34
  "react-dom": "19.2.7",
34
35
  "react-icons": "5.7.0",
35
36
  "react-international-phone": "4.8.0",
37
+ "react-intl": "10.1.20",
36
38
  "react-toastify": "11.1.0",
37
39
  "sass-embedded": "1.100.0",
38
40
  "typescript-plugin-css-modules": "5.2.0",
@@ -109,6 +111,14 @@
109
111
  "./InputPhone": {
110
112
  "types": "./src/components/InputPhone.d.ts",
111
113
  "default": "./src/components/InputPhone.esm.js"
114
+ },
115
+ "./Pagination": {
116
+ "types": "./src/components/Pagination.d.ts",
117
+ "default": "./src/components/Pagination.esm.js"
118
+ },
119
+ "./Carousel": {
120
+ "types": "./src/components/Carousel.d.ts",
121
+ "default": "./src/components/Carousel.esm.js"
112
122
  }
113
123
  }
114
124
  }
@@ -0,0 +1 @@
1
+ @layer craftsman-base{.carousel{position:relative}.carousel__viewport{overflow:hidden}.carousel__container{touch-action:pan-y pinch-zoom;display:flex}.carousel__slide{flex:0 0 100%;min-width:0}.carousel__controls{justify-content:space-between;gap:calc(var(--w-gutter) * .5);margin-top:calc(var(--w-gutter) * .75);flex-flow:row;width:100%;display:inline-flex}.carousel__dots button{margin:0 calc(var(--w-gutter) * .125)}}
@@ -1,9 +1,8 @@
1
1
  import { default as React } from 'react';
2
2
  import { EmblaOptionsType } from 'embla-carousel';
3
3
  import { CarouselPageType } from '../../utilities/types';
4
- export type CarouselProps = {
4
+ export type CarouselProps = Omit<EmblaOptionsType, "slides"> & {
5
5
  slides?: React.ReactNode[];
6
- options?: EmblaOptionsType;
7
6
  className?: string;
8
7
  style?: React.CSSProperties;
9
8
  buttons?: boolean;
@@ -1,32 +1,34 @@
1
1
  @use "../../styles/utilities/functions" as u;
2
2
 
3
- .carousel {
4
- position: relative;
3
+ @layer craftsman-base {
4
+ .carousel {
5
+ position: relative;
5
6
 
6
- &__viewport {
7
- overflow: hidden;
8
- }
7
+ &__viewport {
8
+ overflow: hidden;
9
+ }
9
10
 
10
- &__container {
11
- display: flex;
12
- touch-action: pan-y pinch-zoom;
13
- }
11
+ &__container {
12
+ display: flex;
13
+ touch-action: pan-y pinch-zoom;
14
+ }
14
15
 
15
- &__slide {
16
- flex: 0 0 100%;
17
- min-width: 0;
18
- }
16
+ &__slide {
17
+ flex: 0 0 100%;
18
+ min-width: 0;
19
+ }
19
20
 
20
- &__controls {
21
- display: inline-flex;
22
- justify-content: space-between;
23
- flex-flow: row nowrap;
24
- gap: #{u.width("gutter", 0.5)};
25
- margin-top: #{u.width("gutter", 0.75)};
26
- width: 100%;
27
- }
28
- &__dots button {
29
- margin: 0 #{u.width("gutter", 0.125)};
30
- }
21
+ &__controls {
22
+ display: inline-flex;
23
+ justify-content: space-between;
24
+ flex-flow: row nowrap;
25
+ gap: #{u.width("gutter", 0.5)};
26
+ margin-top: #{u.width("gutter", 0.75)};
27
+ width: 100%;
28
+ }
29
+ &__dots button {
30
+ margin: 0 #{u.width("gutter", 0.125)};
31
+ }
31
32
 
33
+ }
32
34
  }
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { Button as e } from "../Button/Button.esm.js";
3
+ import { Pagination as t } from "../Pagination/Pagination2.esm.js";
4
+ import n from "../../../node_modules/embla-carousel-react/esm/embla-carousel-react.esm.esm.js";
5
+ import './Carousel.css';/* empty css */
6
+ import r from "clsx";
7
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
8
+ import { useCallback as o, useSyncExternalStore as s } from "react";
9
+ import { FaAngleLeft as c, FaAngleRight as l, FaCircle as u, FaRegCircle as d } from "react-icons/fa6";
10
+ //#region src/components/Carousel/Carousel.tsx
11
+ var f = (f) => {
12
+ let { slides: p = [], className: m, style: h, buttons: g = !0, pagination: _ = "dots", ...v } = f, [y, b] = n(v), x = o((e) => b ? (b.on("select", e).on("reInit", e), () => {
13
+ b.off("select", e), b.off("reInit", e);
14
+ }) : () => {}, [b]), S = s(x, () => !!b?.canScrollPrev(), () => !1), C = s(x, () => !!b?.canScrollNext(), () => !1), w = s(x, () => b?.selectedScrollSnap() ?? 0, () => 0), T = () => b?.scrollPrev(), E = () => b?.scrollNext(), D = (e) => {
15
+ let t = parseInt(e.currentTarget.value, 10) || 0;
16
+ b?.scrollTo(t);
17
+ };
18
+ return /* @__PURE__ */ a("div", {
19
+ className: r("carousel", m),
20
+ style: h,
21
+ children: [/* @__PURE__ */ i("div", {
22
+ className: "carousel__viewport",
23
+ ref: y,
24
+ children: /* @__PURE__ */ i("div", {
25
+ className: "carousel__container",
26
+ children: p.map((e, t) => /* @__PURE__ */ i("div", {
27
+ className: "carousel__slide",
28
+ children: e
29
+ }, `slide-${t}`))
30
+ })
31
+ }), /* @__PURE__ */ a("div", {
32
+ className: "carousel__controls",
33
+ children: [
34
+ g && /* @__PURE__ */ a("div", {
35
+ className: "carousel__buttons",
36
+ children: [/* @__PURE__ */ i(e, {
37
+ variant: "text",
38
+ onClick: T,
39
+ disabled: !S,
40
+ name: "prev",
41
+ children: /* @__PURE__ */ i(c, {
42
+ size: 24,
43
+ "aria-label": "prev"
44
+ })
45
+ }), /* @__PURE__ */ i(e, {
46
+ variant: "text",
47
+ onClick: E,
48
+ disabled: !C,
49
+ name: "next",
50
+ children: /* @__PURE__ */ i(l, {
51
+ size: 24,
52
+ "aria-label": "next"
53
+ })
54
+ })]
55
+ }),
56
+ _ === "dots" && /* @__PURE__ */ i("div", {
57
+ className: "carousel__dots",
58
+ children: p.map((t, n) => /* @__PURE__ */ i(e, {
59
+ variant: "text",
60
+ value: n,
61
+ onClick: D,
62
+ children: i(w === n ? u : d, { size: 16 })
63
+ }, `dot-${n}`))
64
+ }),
65
+ _ === "numbers" && /* @__PURE__ */ i(t, {
66
+ total: p.length,
67
+ current: w,
68
+ onChange: D
69
+ })
70
+ ]
71
+ })]
72
+ });
73
+ };
74
+ //#endregion
75
+ export { f as Carousel };
@@ -1 +1 @@
1
- @layer craftsman-base;.input-wrapper label .input-label{margin-bottom:calc(var(--w-gutter) * .25)}.input-wrapper[data-required=true] label .input-label:after{content:"*";color:var(--red700)}.input-wrapper[data-label-position=left] label{align-items:center;display:inline-flex}.input-wrapper[data-label-position=left] label .input-label{width:fit-content;margin-right:calc(var(--w-gutter) * .75)}.input-wrapper[data-label-position=left].textarea label{align-items:flex-start}.input-wrapper[data-label-position=bottom] label{flex-direction:column-reverse;align-items:flex-start;display:inline-flex}.input-wrapper[data-label-position=bottom] label .input-label{margin-bottom:0;margin-top:calc(var(--w-gutter) * .25)}.input-wrapper[data-label-position=right] label{flex-direction:row-reverse;align-items:baseline;display:inline-flex}.input-wrapper[data-label-position=right] label .input-label{margin-left:calc(var(--w-gutter) * .25);margin-bottom:0}.input-wrapper[data-label-position=inside]{--react-international-phone-height:50px;--react-international-phone-border-radius:8px;--react-international-phone-border-color:silver;position:relative}.input-wrapper[data-label-position=inside] .input-label{background-color:var(--white);z-index:1;width:fit-content;top:calc(var(--w-gutter) * .25);left:calc(var(--w-gutter) * .25);pointer-events:none;padding:0 5px;transition:top .15s,left .15s,font-size .15s,color .15s;position:absolute}.input-wrapper[data-label-position=inside]>label>.input-field>.input,.input-wrapper[data-label-position=inside]>.input{transition:border-color .2s}.input-wrapper[data-label-position=inside]:focus-within .input-label{top:-.5rem;left:calc(var(--w-gutter) * .125);font-size:calc(var(--w-text) * .75);color:var(--blue500)}.input-wrapper[data-label-position=inside]:focus-within .input{border-color:var(--blue500)}.input-wrapper[data-label-position=inside]:focus-within .input::placeholder{opacity:1}.input-wrapper[data-label-position=inside][data-has-input=true] .input-label{top:-.5rem;left:calc(var(--w-gutter) * .125);font-size:calc(var(--w-text) * .75)}.input-wrapper[data-label-position=inside] .input[type=tel]{width:100%}.input-wrapper[data-label-position=inside] .input::placeholder{opacity:0;transition:opacity .2s}.input-wrapper .input-field{align-items:center;display:inline-flex;position:relative}.input-wrapper .input-field[data-has-end-adornment=true]>.input{width:100%;padding-right:calc(var(--w-gutter) * 1.5)}.input-wrapper .input-adornment{right:calc(var(--w-gutter) * .5);z-index:1;align-items:center;margin-top:0;display:inline-flex;position:absolute;top:50%;transform:translateY(-50%)}.input-wrapper .input-error{margin-top:calc(var(--w-gutter) * .25);font-size:calc(var(--w-text) * .75);color:var(--red600)}.input-wrapper .input-view-toggle{align-items:"center";padding:0;line-height:1;display:inline-flex}
1
+ @layer craftsman-base{.input-wrapper label .input-label{margin-bottom:calc(var(--w-gutter) * .25)}.input-wrapper[data-required=true] label .input-label:after{content:"*";color:var(--red700)}.input-wrapper[data-label-position=left] label{align-items:center;display:inline-flex}.input-wrapper[data-label-position=left] label .input-label{width:fit-content;margin-right:calc(var(--w-gutter) * .75)}.input-wrapper[data-label-position=left].textarea label{align-items:flex-start}.input-wrapper[data-label-position=bottom] label{flex-direction:column-reverse;align-items:flex-start;display:inline-flex}.input-wrapper[data-label-position=bottom] label .input-label{margin-bottom:0;margin-top:calc(var(--w-gutter) * .25)}.input-wrapper[data-label-position=right] label{flex-direction:row-reverse;align-items:baseline;display:inline-flex}.input-wrapper[data-label-position=right] label .input-label{margin-left:calc(var(--w-gutter) * .25);margin-bottom:0}.input-wrapper[data-label-position=inside]{--react-international-phone-height:50px;--react-international-phone-border-radius:8px;--react-international-phone-border-color:silver;position:relative}.input-wrapper[data-label-position=inside] .input-label{background-color:var(--white);z-index:1;width:fit-content;top:calc(var(--w-gutter) * .25);left:calc(var(--w-gutter) * .25);pointer-events:none;padding:0 5px;transition:top .15s,left .15s,font-size .15s,color .15s;position:absolute}.input-wrapper[data-label-position=inside]>label>.input-field>.input,.input-wrapper[data-label-position=inside]>.input{transition:border-color .2s}.input-wrapper[data-label-position=inside]:focus-within .input-label{top:-.5rem;left:calc(var(--w-gutter) * .125);font-size:calc(var(--w-text) * .75);color:var(--blue500)}.input-wrapper[data-label-position=inside]:focus-within .input{border-color:var(--blue500)}.input-wrapper[data-label-position=inside]:focus-within .input::placeholder{opacity:1}.input-wrapper[data-label-position=inside][data-has-input=true] .input-label{top:-.5rem;left:calc(var(--w-gutter) * .125);font-size:calc(var(--w-text) * .75)}.input-wrapper[data-label-position=inside] .input[type=tel]{width:100%}.input-wrapper[data-label-position=inside] .input::placeholder{opacity:0;transition:opacity .2s}.input-wrapper .input-field{align-items:center;display:inline-flex;position:relative}.input-wrapper .input-field[data-has-end-adornment=true]>.input{width:100%;padding-right:calc(var(--w-gutter) * 1.5)}.input-wrapper .input-adornment{right:calc(var(--w-gutter) * .5);z-index:1;align-items:center;margin-top:0;display:inline-flex;position:absolute;top:50%;transform:translateY(-50%)}.input-wrapper .input-error{margin-top:calc(var(--w-gutter) * .25);font-size:calc(var(--w-text) * .75);color:var(--red600)}.input-wrapper .input-view-toggle{align-items:"center";padding:0;line-height:1;display:inline-flex}}
@@ -6,158 +6,159 @@
6
6
  left: #{u.width("gutter", 0.125)};
7
7
  font-size: #{u.width("text", 0.75)};
8
8
  }
9
-
10
- .input-wrapper {
11
- label {
12
- .input-label {
13
- margin-bottom: #{u.width("gutter", 0.25)};
14
- }
15
- }
16
-
17
- &[data-required="true"] {
9
+ @layer craftsman-base {
10
+ .input-wrapper {
18
11
  label {
19
12
  .input-label {
20
- &::after {
21
- content: "*";
22
- color: var(--red700);
13
+ margin-bottom: #{u.width("gutter", 0.25)};
14
+ }
15
+ }
16
+
17
+ &[data-required="true"] {
18
+ label {
19
+ .input-label {
20
+ &::after {
21
+ content: "*";
22
+ color: var(--red700);
23
+ }
23
24
  }
24
25
  }
25
26
  }
26
- }
27
27
 
28
- &[data-label-position="left"] {
29
- label {
30
- display: inline-flex;
31
- align-items: center;
28
+ &[data-label-position="left"] {
29
+ label {
30
+ display: inline-flex;
31
+ align-items: center;
32
32
 
33
- .input-label {
34
- width: fit-content;
35
- margin-right: #{u.width("gutter", 0.75)};
33
+ .input-label {
34
+ width: fit-content;
35
+ margin-right: #{u.width("gutter", 0.75)};
36
+ }
37
+ }
38
+ &.textarea {
39
+ label {
40
+ align-items: flex-start;
41
+ }
36
42
  }
37
43
  }
38
- &.textarea {
44
+
45
+ &[data-label-position="bottom"] {
39
46
  label {
47
+ display: inline-flex;
48
+ flex-direction: column-reverse;
40
49
  align-items: flex-start;
50
+
51
+ .input-label {
52
+ margin-bottom: 0;
53
+ margin-top: #{u.width("gutter", 0.25)};
54
+ }
41
55
  }
42
56
  }
43
- }
44
57
 
45
- &[data-label-position="bottom"] {
46
- label {
47
- display: inline-flex;
48
- flex-direction: column-reverse;
49
- align-items: flex-start;
58
+ &[data-label-position="right"] {
59
+ label {
60
+ display: inline-flex;
61
+ flex-direction: row-reverse;
62
+ align-items: baseline;
50
63
 
51
- .input-label {
52
- margin-bottom: 0;
53
- margin-top: #{u.width("gutter", 0.25)};
64
+ .input-label {
65
+ margin-left: #{u.width("gutter", 0.25)};
66
+ margin-bottom: 0;
67
+ }
54
68
  }
55
69
  }
56
- }
57
70
 
58
- &[data-label-position="right"] {
59
- label {
60
- display: inline-flex;
61
- flex-direction: row-reverse;
62
- align-items: baseline;
71
+ &[data-label-position="inside"] {
72
+ --react-international-phone-height: 50px;
73
+ --react-international-phone-border-radius: 8px;
74
+ --react-international-phone-border-color: #c0c0c0ff;
75
+ position: relative;
63
76
 
64
77
  .input-label {
65
- margin-left: #{u.width("gutter", 0.25)};
66
- margin-bottom: 0;
78
+ position: absolute;
79
+ background-color: var(--white);
80
+ z-index: 1;
81
+ width: fit-content;
82
+ padding: 0 5px;
83
+ top: #{u.width(gutter, 0.25)};
84
+ left: #{u.width(gutter, 0.25)};
85
+ pointer-events: none;
86
+ transition:
87
+ top 150ms ease,
88
+ left 150ms ease,
89
+ font-size 150ms ease,
90
+ color 150ms ease;
67
91
  }
68
- }
69
- }
70
92
 
71
- &[data-label-position="inside"] {
72
- --react-international-phone-height: 50px;
73
- --react-international-phone-border-radius: 8px;
74
- --react-international-phone-border-color: #c0c0c0ff;
75
- position: relative;
93
+ > label > .input-field > .input,
94
+ > .input {
95
+ transition: border-color 200ms ease;
96
+ }
76
97
 
77
- .input-label {
78
- position: absolute;
79
- background-color: var(--white);
80
- z-index: 1;
81
- width: fit-content;
82
- padding: 0 5px;
83
- top: #{u.width(gutter, 0.25)};
84
- left: #{u.width(gutter, 0.25)};
85
- pointer-events: none;
86
- transition:
87
- top 150ms ease,
88
- left 150ms ease,
89
- font-size 150ms ease,
90
- color 150ms ease;
91
- }
98
+ &:focus-within {
99
+ .input-label {
100
+ @include inside-input-label-floating;
101
+ color: var(--blue500);
102
+ }
92
103
 
93
- > label > .input-field > .input,
94
- > .input {
95
- transition: border-color 200ms ease;
96
- }
104
+ .input {
105
+ border-color: var(--blue500);
106
+ }
97
107
 
98
- &:focus-within {
99
- .input-label {
100
- @include inside-input-label-floating;
101
- color: var(--blue500);
108
+ .input::placeholder {
109
+ opacity: 1;
110
+ }
111
+ }
112
+
113
+ &[data-has-input="true"] {
114
+ .input-label {
115
+ @include inside-input-label-floating;
116
+ }
102
117
  }
103
118
 
104
- .input {
105
- border-color: var(--blue500);
119
+ .input[type="tel"] {
120
+ width: 100%;
106
121
  }
107
122
 
108
123
  .input::placeholder {
109
- opacity: 1;
124
+ opacity: 0;
125
+ transition: opacity 200ms ease;
110
126
  }
111
127
  }
128
+ .input-field {
129
+ position: relative;
130
+ display: inline-flex;
131
+ align-items: center;
112
132
 
113
- &[data-has-input="true"] {
114
- .input-label {
115
- @include inside-input-label-floating;
133
+ &[data-has-end-adornment="true"] {
134
+ > .input {
135
+ width: 100%;
136
+ padding-right: #{u.width("gutter", 1.5)};
137
+ }
116
138
  }
117
139
  }
118
140
 
119
- .input[type="tel"] {
120
- width: 100%;
141
+ .input-adornment {
142
+ position: absolute;
143
+ right: #{u.width("gutter", 0.5)};
144
+ top: 50%;
145
+ transform: translateY(-50%);
146
+ margin-top: 0;
147
+ z-index: 1;
148
+ display: inline-flex;
149
+ align-items: center;
121
150
  }
122
151
 
123
- .input::placeholder {
124
- opacity: 0;
125
- transition: opacity 200ms ease;
152
+ .input-error {
153
+ margin-top: #{u.width("gutter", 0.25)};
154
+ font-size: #{u.width("text", 0.75)};
155
+ color: var(--red600);
126
156
  }
127
- }
128
- .input-field {
129
- position: relative;
130
- display: inline-flex;
131
- align-items: center;
132
-
133
- &[data-has-end-adornment="true"] {
134
- > .input {
135
- width: 100%;
136
- padding-right: #{u.width("gutter", 1.5)};
137
- }
157
+ .input-view-toggle {
158
+ padding: 0;
159
+ display: inline-flex;
160
+ align-items: "center";
161
+ line-height: 1;
138
162
  }
139
163
  }
140
-
141
- .input-adornment {
142
- position: absolute;
143
- right: #{u.width("gutter", 0.5)};
144
- top: 50%;
145
- transform: translateY(-50%);
146
- margin-top: 0;
147
- z-index: 1;
148
- display: inline-flex;
149
- align-items: center;
150
- }
151
-
152
- .input-error {
153
- margin-top: #{u.width("gutter", 0.25)};
154
- font-size: #{u.width("text", 0.75)};
155
- color: var(--red600);
156
- }
157
- .input-view-toggle {
158
- padding: 0;
159
- display: inline-flex;
160
- align-items: "center";
161
- line-height: 1;
162
- }
163
164
  }
@@ -0,0 +1 @@
1
+ @layer craftsman-base{.pagination{align-items:center;gap:calc(var(--w-gutter) * .25);display:inline-flex}.pagination .active{color:var(--gray700);border:1px solid;justify-content:center;align-items:center;width:20px;height:20px;display:inline-flex}}
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ export type PaginationProps = {
3
+ total: number;
4
+ current: number;
5
+ showPages?: number;
6
+ onChange: (e: React.MouseEvent<HTMLButtonElement>) => void;
7
+ className?: string;
8
+ style?: React.CSSProperties;
9
+ };
10
+ export declare const Pagination: React.FC<PaginationProps>;
@@ -0,0 +1,21 @@
1
+
2
+ @use "../../styles/utilities/functions" as u;
3
+
4
+ @layer craftsman-base {
5
+ .pagination {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ gap: #{u.width("gutter", 0.25)};
9
+
10
+ .active {
11
+ color: #{u.color("gray700")};
12
+ border: 1px solid;
13
+ width: 20px;
14
+ height: 20px;
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,66 @@
1
+ "use client";
2
+ import { Button as e } from "../Button/Button.esm.js";
3
+ import './Pagination.css';/* empty css */
4
+ import t from "clsx";
5
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
6
+ import "react";
7
+ //#region src/components/Pagination/Pagination.tsx
8
+ var i = ({ total: i, current: a, onChange: o, className: s, style: c, showPages: l = 5 }) => {
9
+ function u(e, t) {
10
+ let n = Math.floor(l / 2), r = Math.max(0, e - n), i = Math.min(t - 1, r + l - 1);
11
+ r = Math.max(0, i - l + 1);
12
+ let a = [];
13
+ for (let e = r; e <= i; e++) a.push(e);
14
+ return a;
15
+ }
16
+ let d = u(a, i), f = Math.floor(a / l), p = f > 0, m = (f + 1) * l < i, h = (f - 1) * l, g = (f + 1) * l;
17
+ return /* @__PURE__ */ r("nav", {
18
+ className: t("pagination", s),
19
+ style: c,
20
+ "aria-label": "Pagination",
21
+ children: [
22
+ /* @__PURE__ */ n(e, {
23
+ variant: "text",
24
+ value: 0,
25
+ onClick: o,
26
+ disabled: a === 0,
27
+ "aria-label": "First page",
28
+ children: "«"
29
+ }),
30
+ p && /* @__PURE__ */ n(e, {
31
+ variant: "text",
32
+ value: h,
33
+ onClick: o,
34
+ "aria-label": `Back ${l} pages`,
35
+ children: "‹‹"
36
+ }),
37
+ d.map((t) => /* @__PURE__ */ n(e, {
38
+ variant: "text",
39
+ value: t,
40
+ onClick: o,
41
+ disabled: t === a,
42
+ "aria-label": `Page ${t + 1}`,
43
+ "aria-current": t === a ? "page" : void 0,
44
+ className: t === a ? "active" : void 0,
45
+ children: t + 1
46
+ }, t)),
47
+ m && /* @__PURE__ */ n(e, {
48
+ variant: "text",
49
+ value: g,
50
+ onClick: o,
51
+ "aria-label": `Forward ${l} pages`,
52
+ children: "››"
53
+ }),
54
+ /* @__PURE__ */ n(e, {
55
+ variant: "text",
56
+ value: i - 1,
57
+ onClick: o,
58
+ disabled: a === i - 1,
59
+ "aria-label": "Last page",
60
+ children: "»"
61
+ })
62
+ ]
63
+ });
64
+ };
65
+ //#endregion
66
+ export { i as Pagination };
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ import { LabeledInput } from '../Input/InputWrapper';
3
+ type PropType = Omit<React.ComponentProps<"input">, "type"> & LabeledInput & {
4
+ locale?: Intl.LocalesArgument;
5
+ format?: 24 | 12;
6
+ labels?: {
7
+ hour?: "string";
8
+ minute?: "string";
9
+ };
10
+ };
11
+ export declare const TimePicker: (props: PropType) => React.JSX.Element;
12
+ export {};
@@ -0,0 +1,144 @@
1
+ @use "../../styles/utilities/functions" as u;
2
+
3
+ @layer craftsman-base {
4
+
5
+ .timePicker {
6
+ position: relative;
7
+ display: inline-flex;
8
+ height: 160px;
9
+ background-color: #{u.color("white")};
10
+ border: 1px solid #{u.color("gray400")};
11
+ border-radius: 8px;
12
+ z-index: 1;
13
+ &:before,
14
+ &:after {
15
+ position: absolute;
16
+ left: 0;
17
+ right: 0;
18
+ content: "";
19
+ display: block;
20
+ height: calc(50% - 32px / 2);
21
+ z-index: 1;
22
+ pointer-events: none;
23
+ }
24
+
25
+ &:before {
26
+ top: -0.5px;
27
+ border-bottom: 0.5px solid rgba(var(--text-high-contrast-rgb-value), 0.3);
28
+ background: linear-gradient(
29
+ to top,
30
+ rgba(var(--background-site-rgb-value), 0.65) 0%,
31
+ rgba(var(--background-site-rgb-value), 1) 100%
32
+ );
33
+ }
34
+
35
+ &:after {
36
+ bottom: -0.5px;
37
+ border-top: 0.5px solid rgba(var(--text-high-contrast-rgb-value), 0.3);
38
+ background: linear-gradient(
39
+ to bottom,
40
+ rgba(var(--background-site-rgb-value), 0.65) 0%,
41
+ rgba(var(--background-site-rgb-value), 1) 100%
42
+ );
43
+ }
44
+ &__display {
45
+ display: inline-flex;
46
+ align-items: center;
47
+ gap: 2px;
48
+ background-color: white;
49
+ border: 1px solid #{u.color("gray400")};
50
+ border-radius: 4px;
51
+ padding: #{u.width("gutter", 0.25)} 30px #{u.width("gutter", 0.25)} #{u.width("gutter", 0.5)};
52
+ position: relative;
53
+ z-index: 1;
54
+ input, select {
55
+ border: none;
56
+ padding: #{u.width("gutter", 0.25)};
57
+ }
58
+ select::picker-icon {
59
+ display: none;
60
+ }
61
+ .input-view-toggle {
62
+ position: absolute;
63
+ right: #{u.width("gutter", 0.5)};
64
+ }
65
+ }
66
+
67
+ .ios-picker {
68
+ height: 100%;
69
+ display: flex;
70
+ flex-direction: row;
71
+ align-items: center;
72
+ gap: 6px;
73
+ padding: 0 12px;
74
+ line-height: 1;
75
+ font-size: 16px;
76
+
77
+ &--disabled {
78
+ pointer-events: none;
79
+ opacity: 0.5;
80
+ }
81
+ }
82
+
83
+ .ios-picker__scene {
84
+ min-width: 30px;
85
+ height: 100%;
86
+ overflow: hidden;
87
+ display: flex;
88
+ align-items: center;
89
+ touch-action: pan-x;
90
+ }
91
+
92
+ .ios-picker__viewport {
93
+ height: 32px;
94
+ width: 100%;
95
+ perspective: 1000px;
96
+ -webkit-user-select: none;
97
+ -moz-user-select: none;
98
+ -ms-user-select: none;
99
+ user-select: none;
100
+ -webkit-touch-callout: none;
101
+ -khtml-user-select: none;
102
+ -webkit-tap-highlight-color: transparent;
103
+ }
104
+
105
+ .ios-picker__viewport--perspective-left {
106
+ perspective-origin: calc(50% + 130px) 50%;
107
+ transform: translateX(27px);
108
+ }
109
+
110
+ .ios-picker__viewport--perspective-right {
111
+ perspective-origin: calc(50% - 130px) 50%;
112
+ transform: translateX(-27px);
113
+ }
114
+
115
+ .ios-picker__viewport--perspective-center {
116
+ perspective-origin: 50% 50%;
117
+ }
118
+
119
+ .ios-picker__container {
120
+ height: 100%;
121
+ width: 100%;
122
+ transform-style: preserve-3d;
123
+ will-change: transform;
124
+ }
125
+
126
+ .ios-picker__slide {
127
+ width: 100%;
128
+ height: 100%;
129
+ text-align: center;
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ backface-visibility: hidden;
134
+ opacity: 0;
135
+ }
136
+
137
+ .ios-picker__label {
138
+ font-weight: 700;
139
+ white-space: nowrap;
140
+ pointer-events: none;
141
+ }
142
+ }
143
+
144
+ }
@@ -0,0 +1,17 @@
1
+ type TimePickerDisplayProps = {
2
+ hours: number;
3
+ minutes: number;
4
+ hasValue: boolean;
5
+ isReadOnly: boolean;
6
+ timeFormat: 12 | 24;
7
+ amPmLabels: string[] | null;
8
+ visible: boolean;
9
+ onToggleVisible: () => void;
10
+ onChange: {
11
+ hour: (e: React.ChangeEvent<HTMLInputElement>) => void;
12
+ minute: (e: React.ChangeEvent<HTMLInputElement>) => void;
13
+ amPm: (e: React.ChangeEvent<HTMLSelectElement>) => void;
14
+ };
15
+ };
16
+ export declare const TimePickerDisplay: (props: TimePickerDisplayProps) => import("react").JSX.Element;
17
+ export {};
@@ -0,0 +1,13 @@
1
+ import { default as React } from 'react';
2
+ export type IosPickerItemProps = {
3
+ loop?: boolean;
4
+ label: string;
5
+ slideCount: number;
6
+ perspective: "left" | "right" | "center";
7
+ offset?: number;
8
+ slides?: React.ReactNode[];
9
+ selectedIndex?: number;
10
+ onSelect?: (index: number) => void;
11
+ disabled?: boolean;
12
+ };
13
+ export declare const IosPickerItem: (props: IosPickerItemProps) => React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ export declare const CIRCLE_DEGREES = 360;
2
+ export declare const WHEEL_ITEM_SIZE = 32;
3
+ export declare const WHEEL_ITEM_COUNT = 18;
4
+ export declare const WHEEL_ITEMS_IN_VIEW = 4;
5
+ export declare const WHEEL_ITEM_RADIUS: number;
6
+ export declare const IN_VIEW_DEGREES: number;
7
+ export declare const WHEEL_RADIUS: number;
@@ -0,0 +1,14 @@
1
+ import { EmblaCarouselType } from 'embla-carousel';
2
+ export declare const parseTimeString: (val: unknown) => [number, number];
3
+ export declare const padTime: (n: number) => string;
4
+ export declare const toDisplayHour: (hours: number, is12h: boolean) => number;
5
+ export declare const to24Hour: (display: number, isPM: boolean, is12h: boolean) => number;
6
+ export declare const resolveLocale: (locale?: Intl.LocalesArgument) => string;
7
+ export declare const isInView: (wheelLocation: number, slidePosition: number) => boolean;
8
+ export declare const setSlideStyles: (emblaApi: EmblaCarouselType, index: number, loop: boolean, slideCount: number, totalRadius: number) => void;
9
+ export declare const setContainerStyles: (emblaApi: EmblaCarouselType, wheelRotation: number) => void;
10
+ export declare const inactivateEmblaTransform: (emblaApi: EmblaCarouselType) => void;
11
+ export declare const snapOnPointerUp: (emblaApi: EmblaCarouselType) => void;
12
+ export declare const resolveHasValue: (value: React.ComponentProps<"input">["value"], internalHours: number | null) => boolean;
13
+ export declare const resolveTime: (value: unknown, h: number | null, m: number | null) => [number, number];
14
+ export declare const toDisplayInputValue: (hasVal: boolean, val: string | number) => string;
@@ -10,3 +10,5 @@ export { Modal } from './Modal/Modal';
10
10
  export { Select } from './Select/Select';
11
11
  export { DatePicker } from './Input/DatePicker';
12
12
  export { InputPhone } from './Input/InputPhone';
13
+ export { Pagination } from './Pagination/Pagination';
14
+ export { Carousel } from './Carousel/Carousel';
@@ -1,19 +1,21 @@
1
1
 
2
2
  @use "../../styles/utilities/functions" as u;
3
3
 
4
- .carouselWrapper {
5
- width: 400px;
6
- img {
7
- width: 100%;
8
- }
9
- figcaption {
10
- position: absolute;
11
- bottom: 0;
12
- width: 100%;
13
- text-align: center;
14
- font-weight: bold;
15
- background-color: #{u.color("white", rgba, 0.75)};
16
- padding: #{u.width("gutter", 0.5)} 0 #{u.width("gutter")};
17
- }
4
+ @layer craftsman-base {
5
+ .carouselWrapper {
6
+ width: 400px;
7
+ img {
8
+ width: 100%;
9
+ }
10
+ figcaption {
11
+ position: absolute;
12
+ bottom: 0;
13
+ width: 100%;
14
+ text-align: center;
15
+ font-weight: bold;
16
+ background-color: #{u.color("white", rgba, 0.75)};
17
+ padding: #{u.width("gutter", 0.5)} 0 #{u.width("gutter")};
18
+ }
18
19
 
20
+ }
19
21
  }
@@ -61,9 +61,7 @@
61
61
  }
62
62
 
63
63
  input {
64
- &:not([type="radio"]):not([type="checkbox"]) {
65
- @extend %input-base;
66
- }
64
+ @extend %input-base;
67
65
  &[type="checkbox"] {
68
66
  @extend %checkbox;
69
67
  }
@@ -52,6 +52,7 @@ export type TextInputExclusions = z.infer<typeof zTextInputExclusions>;
52
52
  export declare const zTextInputTypes: readonly ["color", "date", "datetime-local", "email", "file", "hidden", "image", "month", "number", "password", "range", "reset", "search", "submit", "tel", "text", "time", "url", "week", "radio", "checkbox"];
53
53
  export declare const zTextInputType: z.ZodEnum<{
54
54
  number: "number";
55
+ month: "month";
55
56
  text: "text";
56
57
  checkbox: "checkbox";
57
58
  radio: "radio";
@@ -62,7 +63,6 @@ export declare const zTextInputType: z.ZodEnum<{
62
63
  file: "file";
63
64
  hidden: "hidden";
64
65
  image: "image";
65
- month: "month";
66
66
  password: "password";
67
67
  range: "range";
68
68
  reset: "reset";
@@ -1,2 +1,5 @@
1
1
  export declare function isEmpty(value: unknown): boolean;
2
2
  export declare function validateEmail(value: string): boolean;
3
+ export declare const getUnitLabel: (locale: Intl.LocalesArgument, unit: Intl.NumberFormatOptions["unit"], override?: string) => string;
4
+ export declare const is24HourFormat: (locale: Intl.LocalesArgument) => boolean;
5
+ export declare const getAmPmLabels: (locale: Intl.LocalesArgument) => [string, string];
@@ -5,5 +5,19 @@ function e(e) {
5
5
  function t(e) {
6
6
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);
7
7
  }
8
+ var n = (e, t, n) => n ?? new Intl.NumberFormat(e, {
9
+ style: "unit",
10
+ unit: t,
11
+ unitDisplay: "long"
12
+ }).formatToParts(1).find((e) => e.type === "unit")?.value ?? String(t), r = (e) => {
13
+ let t = new Intl.DateTimeFormat(e, { hour: "numeric" }).resolvedOptions();
14
+ return t.hourCycle === "h23" || t.hourCycle === "h24";
15
+ }, i = (e) => {
16
+ let t = new Intl.DateTimeFormat(e, {
17
+ hour: "numeric",
18
+ hour12: !0
19
+ });
20
+ return [t.formatToParts(new Date(2e3, 0, 1, 9)).find((e) => e.type === "dayPeriod")?.value ?? "AM", t.formatToParts(new Date(2e3, 0, 1, 21)).find((e) => e.type === "dayPeriod")?.value ?? "PM"];
21
+ };
8
22
  //#endregion
9
- export { e as isEmpty, t as validateEmail };
23
+ export { i as getAmPmLabels, n as getUnitLabel, r as is24HourFormat, e as isEmpty, t as validateEmail };