@bgord/ui 0.2.0 → 0.4.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/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/use-exit-action.d.ts +16 -0
- package/dist/hooks/use-field.d.ts +2 -30
- package/dist/hooks/use-hover.d.ts +11 -0
- package/dist/index.js +161 -26
- package/dist/services/colorful.d.ts +22 -0
- package/dist/services/fields.d.ts +46 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/pluralize.d.ts +11 -0
- package/dist/services/translations.d.ts +16 -0
- package/package.json +5 -2
package/dist/hooks/index.d.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type UseExitActionAnimationType = string;
|
|
3
|
+
type UseExitActionOptionsType = {
|
|
4
|
+
actionFn: () => void;
|
|
5
|
+
animation: UseExitActionAnimationType;
|
|
6
|
+
};
|
|
7
|
+
type UseExitActionReturnType = {
|
|
8
|
+
visible: boolean;
|
|
9
|
+
trigger: (event: React.MouseEvent) => void;
|
|
10
|
+
attach: {
|
|
11
|
+
"data-exit": UseExitActionAnimationType;
|
|
12
|
+
onAnimationEnd: (event: React.AnimationEvent) => void;
|
|
13
|
+
} | undefined;
|
|
14
|
+
};
|
|
15
|
+
export declare function useExitAction(options: UseExitActionOptionsType): UseExitActionReturnType;
|
|
16
|
+
export {};
|
|
@@ -55,6 +55,8 @@ export type useFieldReturnType<T extends FieldValueAllowedTypes> = {
|
|
|
55
55
|
props: {
|
|
56
56
|
id: NewFieldNameType;
|
|
57
57
|
name: NewFieldNameType;
|
|
58
|
+
value: NonNullable<T>;
|
|
59
|
+
onChange: (event: React.ChangeEvent<FieldElementType>) => void;
|
|
58
60
|
};
|
|
59
61
|
};
|
|
60
62
|
/** Whether field value differs from default */
|
|
@@ -113,36 +115,6 @@ export type useFieldReturnType<T extends FieldValueAllowedTypes> = {
|
|
|
113
115
|
* ```
|
|
114
116
|
*/
|
|
115
117
|
export declare function useField<T extends FieldValueAllowedTypes>(config: useFieldConfigType<T>): useFieldReturnType<T>;
|
|
116
|
-
/**
|
|
117
|
-
* Utility class for working with multiple fields
|
|
118
|
-
* @static
|
|
119
|
-
*/
|
|
120
|
-
export declare class Fields {
|
|
121
|
-
/**
|
|
122
|
-
* Check if all fields are unchanged
|
|
123
|
-
* @param {Array<{unchanged: boolean}>} fields - Array of field states
|
|
124
|
-
* @returns {boolean} True if all fields match their default values
|
|
125
|
-
*/
|
|
126
|
-
static allUnchanged(fields: {
|
|
127
|
-
unchanged: boolean;
|
|
128
|
-
}[]): boolean;
|
|
129
|
-
/**
|
|
130
|
-
* Check if any field is unchanged
|
|
131
|
-
* @param {Array<{unchanged: boolean}>} fields - Array of field states
|
|
132
|
-
* @returns {boolean} True if any field matches its default value
|
|
133
|
-
*/
|
|
134
|
-
static anyUnchanged(fields: {
|
|
135
|
-
unchanged: boolean;
|
|
136
|
-
}[]): boolean;
|
|
137
|
-
/**
|
|
138
|
-
* Check if any field has changed
|
|
139
|
-
* @param {Array<{changed: boolean}>} fields - Array of field states
|
|
140
|
-
* @returns {boolean} True if any field differs from its default value
|
|
141
|
-
*/
|
|
142
|
-
static anyChanged(fields: {
|
|
143
|
-
changed: boolean;
|
|
144
|
-
}[]): boolean;
|
|
145
|
-
}
|
|
146
118
|
/**
|
|
147
119
|
* Utility class for working with local fields
|
|
148
120
|
* @static
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type UseHoverConfigType = {
|
|
2
|
+
enabled?: boolean;
|
|
3
|
+
};
|
|
4
|
+
export type UseHoverReturnType<T extends HTMLElement> = {
|
|
5
|
+
attach: {
|
|
6
|
+
ref: React.RefCallback<T | null>;
|
|
7
|
+
};
|
|
8
|
+
isHovering: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function useHover<T extends HTMLElement = HTMLElement>({ enabled, }?: UseHoverConfigType): UseHoverReturnType<T>;
|
|
11
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,24 @@ function Button() {
|
|
|
6
6
|
children: "Click"
|
|
7
7
|
}, undefined, false, undefined, this);
|
|
8
8
|
}
|
|
9
|
+
// src/hooks/use-exit-action.ts
|
|
10
|
+
import React from "react";
|
|
11
|
+
function useExitAction(options) {
|
|
12
|
+
const [phase, setPhase] = React.useState("idle" /* idle */);
|
|
13
|
+
const trigger = (event) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
if (phase === "idle")
|
|
16
|
+
setPhase("exiting" /* exiting */);
|
|
17
|
+
};
|
|
18
|
+
const onAnimationEnd = (event) => {
|
|
19
|
+
if (event.animationName !== options.animation)
|
|
20
|
+
return;
|
|
21
|
+
options.actionFn();
|
|
22
|
+
setPhase("gone" /* gone */);
|
|
23
|
+
};
|
|
24
|
+
const attach = phase === "exiting" ? { "data-exit": options.animation, onAnimationEnd } : undefined;
|
|
25
|
+
return { visible: phase !== "gone", attach, trigger };
|
|
26
|
+
}
|
|
9
27
|
// src/hooks/use-field.ts
|
|
10
28
|
import { useEffect, useState } from "react";
|
|
11
29
|
import { useSearchParams } from "react-router";
|
|
@@ -46,8 +64,8 @@ function useField(config) {
|
|
|
46
64
|
const givenValue = new Field(params.get(config.name));
|
|
47
65
|
const defaultValue = new Field(config.defaultValue);
|
|
48
66
|
const [currentValue, _setCurrentValue] = useState(givenValue.isEmpty() ? defaultValue.get() : givenValue.get());
|
|
49
|
-
const setCurrentValue = (
|
|
50
|
-
const candidate = new Field(
|
|
67
|
+
const setCurrentValue = (value2) => {
|
|
68
|
+
const candidate = new Field(value2);
|
|
51
69
|
_setCurrentValue(candidate.get());
|
|
52
70
|
};
|
|
53
71
|
useEffect(() => {
|
|
@@ -63,56 +81,53 @@ function useField(config) {
|
|
|
63
81
|
}
|
|
64
82
|
if (strategy === "local" /* local */) {}
|
|
65
83
|
}, [currentValue, params, setParams, config.name, strategy]);
|
|
84
|
+
const value = Field.isEmpty(currentValue) ? "" : currentValue;
|
|
85
|
+
const onChange = (event) => setCurrentValue(event.currentTarget.value);
|
|
66
86
|
return {
|
|
67
87
|
strategy,
|
|
68
88
|
defaultValue: defaultValue.get(),
|
|
69
89
|
currentValue,
|
|
70
|
-
value
|
|
90
|
+
value,
|
|
71
91
|
set: setCurrentValue,
|
|
72
|
-
handleChange:
|
|
92
|
+
handleChange: onChange,
|
|
73
93
|
clear: () => setCurrentValue(defaultValue.get()),
|
|
74
94
|
label: { props: { htmlFor: config.name } },
|
|
75
|
-
input: { props: { id: config.name, name: config.name } },
|
|
95
|
+
input: { props: { id: config.name, name: config.name, value, onChange } },
|
|
76
96
|
changed: !Field.compare(currentValue, defaultValue.get()),
|
|
77
97
|
unchanged: Field.compare(currentValue, defaultValue.get()),
|
|
78
98
|
empty: Field.isEmpty(currentValue)
|
|
79
99
|
};
|
|
80
100
|
}
|
|
81
101
|
|
|
82
|
-
class Fields {
|
|
83
|
-
static allUnchanged(fields) {
|
|
84
|
-
return fields.every((field) => field.unchanged);
|
|
85
|
-
}
|
|
86
|
-
static anyUnchanged(fields) {
|
|
87
|
-
return fields.some((field) => field.unchanged);
|
|
88
|
-
}
|
|
89
|
-
static anyChanged(fields) {
|
|
90
|
-
return fields.some((field) => field.changed);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
102
|
class LocalFields {
|
|
95
103
|
static clearAll(fields) {
|
|
96
104
|
return () => fields.forEach((field) => field.clear());
|
|
97
105
|
}
|
|
98
106
|
}
|
|
107
|
+
// src/hooks/use-hover.ts
|
|
108
|
+
import { useCallback, useRef } from "react";
|
|
109
|
+
|
|
99
110
|
// src/hooks/use-toggle.ts
|
|
100
|
-
import {
|
|
111
|
+
import { useState as useState2 } from "react";
|
|
101
112
|
function useToggle({ name, defaultValue = false }) {
|
|
102
|
-
const [on,
|
|
103
|
-
const enable =
|
|
104
|
-
const disable =
|
|
105
|
-
const toggle =
|
|
106
|
-
const off =
|
|
107
|
-
const props =
|
|
113
|
+
const [on, setOn] = useState2(defaultValue);
|
|
114
|
+
const enable = () => setOn(true);
|
|
115
|
+
const disable = () => setOn(false);
|
|
116
|
+
const toggle = () => setOn((v) => !v);
|
|
117
|
+
const off = !on;
|
|
118
|
+
const props = {
|
|
108
119
|
controller: {
|
|
109
120
|
"aria-expanded": on ? "true" : "false",
|
|
110
121
|
"aria-controls": name,
|
|
111
122
|
role: "button",
|
|
112
123
|
tabIndex: 0
|
|
113
124
|
},
|
|
114
|
-
target: {
|
|
115
|
-
|
|
125
|
+
target: {
|
|
126
|
+
id: name,
|
|
127
|
+
role: "region",
|
|
128
|
+
"aria-hidden": on ? "false" : "true"
|
|
129
|
+
}
|
|
130
|
+
};
|
|
116
131
|
return { on, off, enable, disable, toggle, props };
|
|
117
132
|
}
|
|
118
133
|
function extractUseToggle(_props) {
|
|
@@ -122,6 +137,63 @@ function extractUseToggle(_props) {
|
|
|
122
137
|
rest
|
|
123
138
|
};
|
|
124
139
|
}
|
|
140
|
+
|
|
141
|
+
// src/hooks/use-hover.ts
|
|
142
|
+
function useHover({
|
|
143
|
+
enabled = true
|
|
144
|
+
} = {}) {
|
|
145
|
+
const { on: isOn, enable, disable } = useToggle({ name: "is-hovering" });
|
|
146
|
+
const nodeRef = useRef(null);
|
|
147
|
+
const enterEvent = typeof window !== "undefined" && "PointerEvent" in window ? "pointerenter" : "mouseenter";
|
|
148
|
+
const leaveEvent = typeof window !== "undefined" && "PointerEvent" in window ? "pointerleave" : "mouseleave";
|
|
149
|
+
const ref = useCallback((node) => {
|
|
150
|
+
const prev = nodeRef.current;
|
|
151
|
+
if (prev) {
|
|
152
|
+
prev.removeEventListener(enterEvent, enable);
|
|
153
|
+
prev.removeEventListener(leaveEvent, disable);
|
|
154
|
+
}
|
|
155
|
+
nodeRef.current = node;
|
|
156
|
+
if (node && enabled) {
|
|
157
|
+
node.addEventListener(enterEvent, enable);
|
|
158
|
+
node.addEventListener(leaveEvent, disable);
|
|
159
|
+
}
|
|
160
|
+
}, [enterEvent, leaveEvent, enabled, enable, disable]);
|
|
161
|
+
return {
|
|
162
|
+
attach: { ref },
|
|
163
|
+
isHovering: isOn && enabled
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// src/services/colorful.ts
|
|
167
|
+
function Colorful(color) {
|
|
168
|
+
const value = `var(--${color})`;
|
|
169
|
+
const options = {
|
|
170
|
+
color: { color: value },
|
|
171
|
+
background: { background: value }
|
|
172
|
+
};
|
|
173
|
+
const style = {
|
|
174
|
+
color: { style: { color: value } },
|
|
175
|
+
background: { style: { background: value } }
|
|
176
|
+
};
|
|
177
|
+
return { ...options, style };
|
|
178
|
+
}
|
|
179
|
+
// src/services/fields.ts
|
|
180
|
+
class Fields {
|
|
181
|
+
static allUnchanged(fields) {
|
|
182
|
+
return fields.every((field) => field.unchanged);
|
|
183
|
+
}
|
|
184
|
+
static allEmpty(fields) {
|
|
185
|
+
return fields.every((field) => field.empty);
|
|
186
|
+
}
|
|
187
|
+
static anyEmpty(fields) {
|
|
188
|
+
return fields.some((field) => field.empty);
|
|
189
|
+
}
|
|
190
|
+
static anyUnchanged(fields) {
|
|
191
|
+
return fields.some((field) => field.unchanged);
|
|
192
|
+
}
|
|
193
|
+
static anyChanged(fields) {
|
|
194
|
+
return fields.some((field) => field.changed);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
125
197
|
// src/services/form.ts
|
|
126
198
|
class Form {
|
|
127
199
|
static inputPattern(config) {
|
|
@@ -145,6 +217,24 @@ class Form {
|
|
|
145
217
|
return { required };
|
|
146
218
|
}
|
|
147
219
|
}
|
|
220
|
+
// src/services/pluralize.ts
|
|
221
|
+
import { polishPlurals } from "polish-plurals";
|
|
222
|
+
function pluralize(options) {
|
|
223
|
+
if (options.language === "en" /* en */) {
|
|
224
|
+
const plural = options.plural ?? `${options.singular}s`;
|
|
225
|
+
if (options.value === 1)
|
|
226
|
+
return options.singular;
|
|
227
|
+
return plural;
|
|
228
|
+
}
|
|
229
|
+
if (options.language === "pl" /* pl */) {
|
|
230
|
+
const value = options.value ?? 1;
|
|
231
|
+
if (value === 1)
|
|
232
|
+
return options.singular;
|
|
233
|
+
return polishPlurals(options.singular, String(options.plural), String(options.genitive), value);
|
|
234
|
+
}
|
|
235
|
+
console.warn(`[@bgord/frontend] missing pluralization function for language: ${options.language}.`);
|
|
236
|
+
return options.singular;
|
|
237
|
+
}
|
|
148
238
|
// src/services/rhythm.ts
|
|
149
239
|
var DEFAULT_BASE_PX = 12;
|
|
150
240
|
function Rhythm(base = DEFAULT_BASE_PX) {
|
|
@@ -176,15 +266,60 @@ function Rhythm(base = DEFAULT_BASE_PX) {
|
|
|
176
266
|
function px(number) {
|
|
177
267
|
return `${number}px`;
|
|
178
268
|
}
|
|
269
|
+
// src/services/translations.tsx
|
|
270
|
+
import { createContext, use, useCallback as useCallback2 } from "react";
|
|
271
|
+
var TranslationsContext = createContext({
|
|
272
|
+
translations: {},
|
|
273
|
+
language: "en"
|
|
274
|
+
});
|
|
275
|
+
function useTranslations() {
|
|
276
|
+
const value = use(TranslationsContext);
|
|
277
|
+
if (value === undefined) {
|
|
278
|
+
throw new Error("useTranslations must be used within the TranslationsContext");
|
|
279
|
+
}
|
|
280
|
+
const translate = useCallback2((key, variables) => {
|
|
281
|
+
const translation = value.translations[key];
|
|
282
|
+
if (!translation) {
|
|
283
|
+
console.warn(`[@bgord/ui] missing translation for key: ${key}`);
|
|
284
|
+
return key;
|
|
285
|
+
}
|
|
286
|
+
if (!variables)
|
|
287
|
+
return translation;
|
|
288
|
+
return Object.entries(variables).reduce((result, [placeholder, value2]) => {
|
|
289
|
+
const regex = new RegExp(`{{${placeholder}}}`, "g");
|
|
290
|
+
return result.replace(regex, String(value2));
|
|
291
|
+
}, translation);
|
|
292
|
+
}, [value.translations]);
|
|
293
|
+
return translate;
|
|
294
|
+
}
|
|
295
|
+
function useLanguage() {
|
|
296
|
+
const value = use(TranslationsContext);
|
|
297
|
+
if (value === undefined) {
|
|
298
|
+
throw new Error("useLanguage must be used within the TranslationsContext");
|
|
299
|
+
}
|
|
300
|
+
return value.language;
|
|
301
|
+
}
|
|
302
|
+
function usePluralize() {
|
|
303
|
+
const language = useLanguage();
|
|
304
|
+
return (options) => pluralize({ ...options, language });
|
|
305
|
+
}
|
|
179
306
|
export {
|
|
307
|
+
useTranslations,
|
|
180
308
|
useToggle,
|
|
309
|
+
usePluralize,
|
|
310
|
+
useLanguage,
|
|
311
|
+
useHover,
|
|
181
312
|
useFieldStrategyEnum,
|
|
182
313
|
useField,
|
|
314
|
+
useExitAction,
|
|
315
|
+
pluralize,
|
|
183
316
|
extractUseToggle,
|
|
317
|
+
TranslationsContext,
|
|
184
318
|
Rhythm,
|
|
185
319
|
LocalFields,
|
|
186
320
|
Form,
|
|
187
321
|
Fields,
|
|
188
322
|
Field,
|
|
323
|
+
Colorful,
|
|
189
324
|
Button
|
|
190
325
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type ColorType = string;
|
|
2
|
+
export declare function Colorful(color: ColorType): {
|
|
3
|
+
style: {
|
|
4
|
+
color: {
|
|
5
|
+
style: {
|
|
6
|
+
color: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
background: {
|
|
10
|
+
style: {
|
|
11
|
+
background: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
color: {
|
|
16
|
+
color: string;
|
|
17
|
+
};
|
|
18
|
+
background: {
|
|
19
|
+
background: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility class for working with multiple fields
|
|
3
|
+
* @static
|
|
4
|
+
*/
|
|
5
|
+
export declare class Fields {
|
|
6
|
+
/**
|
|
7
|
+
* Check if all fields are unchanged
|
|
8
|
+
* @param {Array<{unchanged: boolean}>} fields - Array of field states
|
|
9
|
+
* @returns {boolean} True if all fields match their default values
|
|
10
|
+
*/
|
|
11
|
+
static allUnchanged(fields: {
|
|
12
|
+
unchanged: boolean;
|
|
13
|
+
}[]): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Check if all fields are empty
|
|
16
|
+
* @param {Array<{empty: boolean}>} fields - Array of field states
|
|
17
|
+
* @returns {boolean} True if all fields are empty
|
|
18
|
+
*/
|
|
19
|
+
static allEmpty(fields: {
|
|
20
|
+
empty: boolean;
|
|
21
|
+
}[]): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Check if any field is empty
|
|
24
|
+
* @param {Array<{empty: boolean}>} fields - Array of field states
|
|
25
|
+
* @returns {boolean} True if any field is empty
|
|
26
|
+
*/
|
|
27
|
+
static anyEmpty(fields: {
|
|
28
|
+
empty: boolean;
|
|
29
|
+
}[]): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if any field is unchanged
|
|
32
|
+
* @param {Array<{unchanged: boolean}>} fields - Array of field states
|
|
33
|
+
* @returns {boolean} True if any field matches its default value
|
|
34
|
+
*/
|
|
35
|
+
static anyUnchanged(fields: {
|
|
36
|
+
unchanged: boolean;
|
|
37
|
+
}[]): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Check if any field has changed
|
|
40
|
+
* @param {Array<{changed: boolean}>} fields - Array of field states
|
|
41
|
+
* @returns {boolean} True if any field differs from its default value
|
|
42
|
+
*/
|
|
43
|
+
static anyChanged(fields: {
|
|
44
|
+
changed: boolean;
|
|
45
|
+
}[]): boolean;
|
|
46
|
+
}
|
package/dist/services/index.d.ts
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type PluralizeWordType = string;
|
|
2
|
+
type PluralizeValueType = number | null | undefined;
|
|
3
|
+
export type PluralizeOptionsType = {
|
|
4
|
+
value: PluralizeValueType;
|
|
5
|
+
singular: PluralizeWordType;
|
|
6
|
+
plural?: PluralizeWordType;
|
|
7
|
+
genitive?: PluralizeWordType;
|
|
8
|
+
language: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function pluralize(options: PluralizeOptionsType): PluralizeWordType;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PluralizeOptionsType } from "./pluralize";
|
|
2
|
+
export type TranslationsKeyType = string;
|
|
3
|
+
export type TranslationsValueType = string;
|
|
4
|
+
export type TranslationsType = Record<TranslationsKeyType, TranslationsValueType>;
|
|
5
|
+
type TranslationPlaceholderType = string;
|
|
6
|
+
type TranslationPlaceholderValueType = string | number;
|
|
7
|
+
type TranslationVariableType = Record<TranslationPlaceholderType, TranslationPlaceholderValueType>;
|
|
8
|
+
export type TranslationsContextValueType = {
|
|
9
|
+
translations: TranslationsType;
|
|
10
|
+
language: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const TranslationsContext: import("react").Context<TranslationsContextValueType>;
|
|
13
|
+
export declare function useTranslations(): (key: TranslationsKeyType, variables?: TranslationVariableType) => string;
|
|
14
|
+
export declare function useLanguage(): TranslationsContextValueType["language"];
|
|
15
|
+
export declare function usePluralize(): (options: Omit<PluralizeOptionsType, "language">) => string;
|
|
16
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgord/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -38,8 +38,11 @@
|
|
|
38
38
|
"@commitlint/config-conventional": "19.8.1",
|
|
39
39
|
"cspell": "9.1.3",
|
|
40
40
|
"knip": "5.61.3",
|
|
41
|
-
"lefthook": "1.
|
|
41
|
+
"lefthook": "1.12.2",
|
|
42
42
|
"only-allow": "1.2.1",
|
|
43
43
|
"shellcheck": "3.1.0"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"polish-plurals": "^1.1.0"
|
|
44
47
|
}
|
|
45
48
|
}
|