@ryuzaki13/react-foundation-lib 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +154 -100
  2. package/dist/chunks/{formatters-CBgrBeSA.js → formatters-D33Uh3si.js} +7 -8
  3. package/dist/chunks/{formatters-CBgrBeSA.js.map → formatters-D33Uh3si.js.map} +1 -1
  4. package/dist/chunks/odata-service-CWkHASdw.js +2211 -0
  5. package/dist/chunks/odata-service-CWkHASdw.js.map +1 -0
  6. package/dist/chunks/{validate-BW-7BZy_.js → validate-DyzojM0w.js} +2 -2
  7. package/dist/chunks/{validate-BW-7BZy_.js.map → validate-DyzojM0w.js.map} +1 -1
  8. package/dist/error-report/index.js +2 -2
  9. package/dist/excel/index.js +20 -21
  10. package/dist/excel/index.js.map +1 -1
  11. package/dist/formatters/index.js +4 -5
  12. package/dist/{odata → odata-service}/buildSeparatedArrays.d.ts +1 -1
  13. package/dist/odata-service/buildSeparatedArrays.d.ts.map +1 -0
  14. package/dist/{odata/types.d.ts → odata-service/collectionTypes.d.ts} +1 -1
  15. package/dist/odata-service/collectionTypes.d.ts.map +1 -0
  16. package/dist/{odata → odata-service}/dateUtils.d.ts +1 -1
  17. package/dist/odata-service/dateUtils.d.ts.map +1 -0
  18. package/dist/{odata → odata-service}/findCollectionPairs.d.ts +1 -1
  19. package/dist/odata-service/findCollectionPairs.d.ts.map +1 -0
  20. package/dist/odata-service/index.d.ts +5 -0
  21. package/dist/odata-service/index.d.ts.map +1 -1
  22. package/dist/odata-service/index.js +2 -2
  23. package/dist/{odata/store.d.ts → odata-service/odataCollectionStore.d.ts} +1 -1
  24. package/dist/odata-service/odataCollectionStore.d.ts.map +1 -0
  25. package/dist/query-client/index.js.map +1 -1
  26. package/dist/query-client/queryClient.d.ts +1 -3
  27. package/dist/query-client/queryClient.d.ts.map +1 -1
  28. package/package.json +1 -5
  29. package/src/bounded-copy-stack/README.mdx +257 -0
  30. package/src/context-menu/README.mdx +326 -0
  31. package/src/currency/README.mdx +155 -0
  32. package/src/date-segments/README.mdx +319 -0
  33. package/src/error/README.mdx +95 -0
  34. package/src/error-report/README.mdx +612 -0
  35. package/src/excel/README.mdx +449 -0
  36. package/src/fingerprint/README.mdx +100 -0
  37. package/src/form/README.mdx +101 -0
  38. package/src/foundationLib.mdx +70 -54
  39. package/src/hooks/README.mdx +988 -0
  40. package/src/http/README.mdx +126 -0
  41. package/src/media/README.mdx +1853 -0
  42. package/src/notifications/README.mdx +323 -0
  43. package/src/number-scale/README.mdx +318 -0
  44. package/src/odata-service/README.mdx +2916 -0
  45. package/src/presets/README.mdx +136 -0
  46. package/src/pwa/README.mdx +394 -0
  47. package/src/query-client/README.mdx +2500 -0
  48. package/src/range-output/README.mdx +161 -0
  49. package/src/seo/README.mdx +279 -0
  50. package/src/session-storage/README.mdx +125 -0
  51. package/src/string-comparison/README.mdx +96 -0
  52. package/src/table/README.mdx +3106 -0
  53. package/src/three-scene/README.mdx +248 -0
  54. package/src/types/README.mdx +172 -0
  55. package/src/utils/README.mdx +1724 -0
  56. package/src/validators/README.mdx +239 -0
  57. package/src/virtualizer/README.mdx +138 -0
  58. package/src/xml/README.mdx +149 -0
  59. package/dist/chunks/dateUtils-B5V5aXB-.js +0 -847
  60. package/dist/chunks/dateUtils-B5V5aXB-.js.map +0 -1
  61. package/dist/chunks/odata-service-B6TxBSLm.js +0 -1277
  62. package/dist/chunks/odata-service-B6TxBSLm.js.map +0 -1
  63. package/dist/odata/buildSeparatedArrays.d.ts.map +0 -1
  64. package/dist/odata/dateUtils.d.ts.map +0 -1
  65. package/dist/odata/findCollectionPairs.d.ts.map +0 -1
  66. package/dist/odata/index.d.ts +0 -6
  67. package/dist/odata/index.d.ts.map +0 -1
  68. package/dist/odata/index.js +0 -102
  69. package/dist/odata/index.js.map +0 -1
  70. package/dist/odata/store.d.ts.map +0 -1
  71. package/dist/odata/types.d.ts.map +0 -1
  72. package/src/pwa/README.md +0 -638
  73. package/src/query-client/README.md +0 -565
@@ -0,0 +1,319 @@
1
+ import { Meta } from "@storybook/addon-docs/blocks";
2
+
3
+ <Meta title="Foundation Lib/Date/Segments" />
4
+
5
+ # Сегменты даты и времени через `@ryuzaki13/react-foundation-lib/date-segments`
6
+
7
+ Модуль разбирает строковую маску на редактируемые сегменты и литералы, преобразует `Date` в значения полей и строго собирает `Date` обратно. Он предназначен для segment-based date/time inputs.
8
+
9
+ Документ объясняет маски, индексы, local time, двухзначный год, неполный ввод и все публичные типы.
10
+
11
+ ## Содержание
12
+
13
+ - [Назначение и границы](#назначение-и-границы)
14
+ - [Установка и импорт](#установка-и-импорт)
15
+ - [Базовые понятия](#базовые-понятия)
16
+ - [Поддерживаемые tokens](#поддерживаемые-tokens)
17
+ - [`parseDateSegmentMask`](#parsedatesegmentmask)
18
+ - [`isEditableDateSegment`](#iseditabledatesegment)
19
+ - [`dateToIndexedSegmentValues`](#datetoindexedsegmentvalues)
20
+ - [`areAllDateSegmentsEmpty`](#arealldatesegmentsempty)
21
+ - [`indexedSegmentsToDate`](#indexedsegmentstodate)
22
+ - [Полный input pipeline](#полный-input-pipeline)
23
+ - [Local time и календарная семантика](#local-time-и-календарная-семантика)
24
+ - [API и ошибки](#api-и-ошибки)
25
+
26
+ ## Назначение и границы
27
+
28
+ ```text
29
+ "dd.MM.yyyy HH:mm"
30
+ │ parseDateSegmentMask
31
+ ▼
32
+ [day, ".", month, ".", year, " ", hours, ":", minutes]
33
+ │ пользователь вводит значения по индексам
34
+ ▼
35
+ Map { 0:"03", 2:"03", 4:"2026", 6:"18", 8:"03" }
36
+ │ indexedSegmentsToDate
37
+ ▼
38
+ new Date(2026, 2, 3, 18, 3)
39
+ ```
40
+
41
+ Модуль не рендерит input, не управляет focus/caret, не форматирует итоговую дату для UI/backend и не парсит ISO/OData строку. Для общего парсинга/форматирования используйте `/formatters`.
42
+
43
+ ## Установка и импорт
44
+
45
+ ```ts
46
+ import {
47
+ areAllDateSegmentsEmpty,
48
+ dateToIndexedSegmentValues,
49
+ indexedSegmentsToDate,
50
+ isEditableDateSegment,
51
+ parseDateSegmentMask
52
+ } from "@ryuzaki13/react-foundation-lib/date-segments";
53
+
54
+ import type {
55
+ DateMaskSegment,
56
+ DateSegmentId,
57
+ EditableDateSegment,
58
+ LiteralDateSegment
59
+ } from "@ryuzaki13/react-foundation-lib/date-segments";
60
+ ```
61
+
62
+ Модуль не зависит от React/DOM и использует локальные getters/constructor стандартного `Date`.
63
+
64
+ ## Базовые понятия
65
+
66
+ Маска состоит из двух видов сегментов.
67
+
68
+ Editable segment:
69
+
70
+ ```ts
71
+ interface EditableDateSegment {
72
+ kind: "editable";
73
+ id: DateSegmentId;
74
+ token: string;
75
+ length: number;
76
+ min: number;
77
+ max: number;
78
+ placeholder: string;
79
+ ariaLabel: string;
80
+ }
81
+ ```
82
+
83
+ Literal segment:
84
+
85
+ ```ts
86
+ interface LiteralDateSegment {
87
+ kind: "literal";
88
+ text: string;
89
+ }
90
+ ```
91
+
92
+ Литералы — точки, двоеточия, пробелы, слова и неподдерживаемые символы маски. Они показываются пользователю, но не имеют редактируемого значения.
93
+
94
+ `DateMaskSegment` — discriminated union. Поле `kind` позволяет безопасно сузить тип.
95
+
96
+ ## Поддерживаемые tokens
97
+
98
+ | Token | `id` | Длина | Диапазон | Placeholder | ARIA label |
99
+ | ------ | --------- | ----: | -------- | ----------- | ---------- |
100
+ | `YYYY` | `year` | 4 | 1–9999 | `ГГГГ` | Год |
101
+ | `yyyy` | `year` | 4 | 1–9999 | `гггг` | Год |
102
+ | `yy` | `year` | 2 | 0–99 | `гг` | Год |
103
+ | `MM` | `month` | 2 | 1–12 | `мм` | Месяц |
104
+ | `DD` | `day` | 2 | 1–31 | `ДД` | День |
105
+ | `dd` | `day` | 2 | 1–31 | `дд` | День |
106
+ | `hh` | `hours` | 2 | 0–23 | `чч` | Часы |
107
+ | `HH` | `hours` | 2 | 0–23 | `чч` | Часы |
108
+ | `mm` | `minutes` | 2 | 0–59 | `мм` | Минуты |
109
+ | `ss` | `seconds` | 2 | 0–59 | `сс` | Секунды |
110
+
111
+ Обратите внимание:
112
+
113
+ - `MM` — месяц, `mm` — минуты;
114
+ - `dd`/`DD` — день;
115
+ - `hh` и `HH` оба означают 24-часовой диапазон `0..23`;
116
+ - одиночные `d`, `M`, `H`, `m`, `s` не являются tokens и станут literal text;
117
+ - tokens регистрозависимы.
118
+
119
+ ## `parseDateSegmentMask`
120
+
121
+ ```ts
122
+ const segments = parseDateSegmentMask("dd.MM.yyyy HH:mm");
123
+ ```
124
+
125
+ Результат:
126
+
127
+ ```ts
128
+ [
129
+ { kind: "editable", id: "day", token: "dd", length: 2 /* ... */ },
130
+ { kind: "literal", text: "." },
131
+ { kind: "editable", id: "month", token: "MM", length: 2 /* ... */ },
132
+ { kind: "literal", text: "." },
133
+ { kind: "editable", id: "year", token: "yyyy", length: 4 /* ... */ },
134
+ { kind: "literal", text: " " },
135
+ { kind: "editable", id: "hours", token: "HH", length: 2 /* ... */ },
136
+ { kind: "literal", text: ":" },
137
+ { kind: "editable", id: "minutes", token: "mm", length: 2 /* ... */ }
138
+ ];
139
+ ```
140
+
141
+ Соседние literal characters объединяются в один segment:
142
+
143
+ ```ts
144
+ parseDateSegmentMask("Дата: dd");
145
+ // literal "Дата: ", затем editable day.
146
+ ```
147
+
148
+ Пустая маска возвращает пустой массив. Parser не валидирует обязательность года/месяца/дня и допускает повтор одного `id`; корректный набор задаёт host.
149
+
150
+ ## `isEditableDateSegment`
151
+
152
+ Type guard:
153
+
154
+ ```ts
155
+ const editable = segments.filter(isEditableDateSegment);
156
+ // EditableDateSegment[].
157
+ ```
158
+
159
+ Он проверяет только `segment.kind === "editable"` и рассчитан на значения, полученные из parser или соответствующие публичному типу.
160
+
161
+ ## `dateToIndexedSegmentValues`
162
+
163
+ Функция создаёт `Map<number, string>`, где key — индекс segment в полном массиве, включая literals.
164
+
165
+ ```ts
166
+ const segments = parseDateSegmentMask("dd.MM.yyyy HH:mm");
167
+ const values = dateToIndexedSegmentValues(new Date(2026, 2, 3, 18, 3), segments);
168
+
169
+ console.log([...values.entries()]);
170
+ // [[0,"03"], [2,"03"], [4,"2026"], [6,"18"], [8,"03"]]
171
+ ```
172
+
173
+ Индексы `1`, `3`, `5`, `7` отсутствуют, потому что это literals.
174
+
175
+ Для `null`, `undefined` или invalid `Date` каждая editable позиция получает `""`:
176
+
177
+ ```ts
178
+ dateToIndexedSegmentValues(null, segments);
179
+ dateToIndexedSegmentValues(new Date("bad"), segments);
180
+ ```
181
+
182
+ Двухзначный year берёт последние две цифры полного года. Значения дополняются нулями слева до длины segment.
183
+
184
+ ## `areAllDateSegmentsEmpty`
185
+
186
+ ```ts
187
+ const empty = areAllDateSegmentsEmpty(segments, values);
188
+ ```
189
+
190
+ Возвращает `true`, если у каждого editable segment значение отсутствует или является пустой строкой.
191
+
192
+ Literal segments игнорируются. Строка из пробелов truthy и считается заполненной. Функция не проверяет длину/числовой формат.
193
+
194
+ ## `indexedSegmentsToDate`
195
+
196
+ ```ts
197
+ const date = indexedSegmentsToDate(segments, values);
198
+ ```
199
+
200
+ Возвращает строгий valid `Date` или `null`.
201
+
202
+ ### Полный ввод
203
+
204
+ Каждый editable segment должен иметь строку длиной не меньше `segment.length`:
205
+
206
+ ```ts
207
+ values.set(dayIndex, "3"); // Для token dd длины недостаточно.
208
+ indexedSegmentsToDate(segments, values); // null.
209
+ ```
210
+
211
+ Проверка длины не гарантирует numeric input. `"ab"` пройдёт length-check, но `parseInt` не даст valid component и итог будет `null`.
212
+
213
+ ### Календарная строгость
214
+
215
+ После сборки компоненты сверяются с полученным `Date`:
216
+
217
+ ```ts
218
+ // 31 февраля не превращается автоматически в март.
219
+ indexedSegmentsToDate(segments, february31Values); // null.
220
+
221
+ // 24:00 не принимается.
222
+ indexedSegmentsToDate(segments, hour24Values); // null.
223
+ ```
224
+
225
+ Проверяются month `1..12`, day `1..31`, hours `0..23`, minutes/seconds `0..59`, а затем реальная календарная дата.
226
+
227
+ ### Отсутствующие части и `defaultDate`
228
+
229
+ Если маска не содержит некоторый тип segment, компонент берётся из `defaultDate`:
230
+
231
+ ```ts
232
+ const timeSegments = parseDateSegmentMask("HH:mm");
233
+ const values = new Map([
234
+ [0, "09"],
235
+ [2, "15"]
236
+ ]);
237
+
238
+ const result = indexedSegmentsToDate(timeSegments, values, {
239
+ defaultDate: new Date(2026, 2, 3, 18, 3)
240
+ });
241
+ // 3 марта 2026, 09:15 local time.
242
+ ```
243
+
244
+ Default fallback без options — `new Date(1970, 0, 1, 0, 0, 0, 0)` в local timezone.
245
+
246
+ `defaultDate` не валидируется отдельно. Invalid fallback обычно приведёт к `null` после итоговой проверки.
247
+
248
+ ### Двухзначный год
249
+
250
+ Для token `yy`:
251
+
252
+ | Ввод | Полный год |
253
+ | --------- | ---------: |
254
+ | `00`–`69` | 2000–2069 |
255
+ | `70`–`99` | 1970–1999 |
256
+
257
+ Порог фиксирован и не зависит от текущего года.
258
+
259
+ ## Полный input pipeline
260
+
261
+ ```tsx
262
+ const segments = parseDateSegmentMask("dd.MM.yyyy");
263
+ const [values, setValues] = useState(() => dateToIndexedSegmentValues(initialDate, segments));
264
+
265
+ function updateSegment(index: number, value: string) {
266
+ setValues((current) => {
267
+ const next = new Map(current);
268
+ next.set(index, value);
269
+ return next;
270
+ });
271
+ }
272
+
273
+ function commit() {
274
+ if (areAllDateSegmentsEmpty(segments, values)) {
275
+ onChange(null);
276
+ return;
277
+ }
278
+
279
+ const nextDate = indexedSegmentsToDate(segments, values);
280
+ if (nextDate === null) {
281
+ showValidationError("Введите корректную дату");
282
+ return;
283
+ }
284
+
285
+ onChange(nextDate);
286
+ }
287
+ ```
288
+
289
+ UI самостоятельно отвечает за digits-only input, `aria-label`, focus movement, paste, validation message и controlled state.
290
+
291
+ ## Local time и календарная семантика
292
+
293
+ Функции используют `getFullYear`, `getMonth`, `getDate`, `getHours` и `new Date(year, ...)`. Это local calendar/time, а не UTC.
294
+
295
+ Не смешивайте segment input с автоматическим timezone conversion. Перед отправкой в OData/backend примените formatter, соответствующий контракту API.
296
+
297
+ ## API и ошибки
298
+
299
+ | Export | Результат |
300
+ | ---------------------------- | ------------------------------------ |
301
+ | `parseDateSegmentMask` | `DateMaskSegment[]` |
302
+ | `isEditableDateSegment` | Type guard |
303
+ | `dateToIndexedSegmentValues` | `Map<number, string>` |
304
+ | `areAllDateSegmentsEmpty` | `boolean` |
305
+ | `indexedSegmentsToDate` | `Date \| null` |
306
+ | `DateSegmentId` | year/month/day/hours/minutes/seconds |
307
+ | `EditableDateSegment` | Metadata редактируемого segment |
308
+ | `LiteralDateSegment` | `{ kind: "literal"; text: string }` |
309
+ | `DateMaskSegment` | Union editable/literal |
310
+
311
+ Типичные ошибки:
312
+
313
+ - Перепутать `MM` и `mm`.
314
+ - Хранить values по порядковому номеру editable fields вместо полного segment index.
315
+ - Передать однозначную строку `"3"` для token длины `2` и ожидать автодополнение при сборке.
316
+ - Ожидать UTC.
317
+ - Считать `min/max` metadata заменой UI input validation.
318
+ - Использовать произвольную маску с повторяющимися year/month segments.
319
+ - Нормализовать пользовательский partial draft до команды commit.
@@ -0,0 +1,95 @@
1
+ import { Meta } from "@storybook/addon-docs/blocks";
2
+
3
+ <Meta title="Foundation Lib/Errors/Missing Context" />
4
+
5
+ # Ошибка отсутствующего React context через `@ryuzaki13/react-foundation-lib/error`
6
+
7
+ Модуль создаёт единообразное диагностическое сообщение, когда custom hook вызван вне нужного Provider.
8
+
9
+ ## Установка и импорт
10
+
11
+ ```ts
12
+ import { createMissingContextError, createMissingContextErrorMessage } from "@ryuzaki13/react-foundation-lib/error";
13
+ ```
14
+
15
+ Модуль не импортирует React и может использоваться в любом TypeScript runtime. Типы объектов options выводятся из аргумента и отдельно не экспортируются.
16
+
17
+ ## Когда использовать
18
+
19
+ Обычная схема context hook:
20
+
21
+ ```tsx
22
+ const DemoContext = createContext<DemoValue | null>(null);
23
+
24
+ export function useDemo() {
25
+ const value = useContext(DemoContext);
26
+
27
+ if (value === null) {
28
+ throw createMissingContextError({
29
+ hookName: "useDemo",
30
+ providerName: "DemoProvider",
31
+ contextName: "DemoContext"
32
+ });
33
+ }
34
+
35
+ return value;
36
+ }
37
+ ```
38
+
39
+ Ошибка должна выбрасываться при нарушении обязательного structural contract. Если отсутствие Provider является допустимым состоянием, возвращайте optional result по собственному явно названному API.
40
+
41
+ ## `createMissingContextErrorMessage`
42
+
43
+ ```ts
44
+ const message = createMissingContextErrorMessage({
45
+ hookName: "useDemo",
46
+ providerName: "DemoProvider",
47
+ contextName: "DemoContext"
48
+ });
49
+ ```
50
+
51
+ Результат состоит из трёх предложений:
52
+
53
+ 1. Где разрешено использовать hook.
54
+ 2. Какой context оказался `null`.
55
+ 3. Что Provider должен находиться выше вызывающего компонента.
56
+
57
+ `contextName` optional. Без него сообщение остаётся корректным, но менее конкретным.
58
+
59
+ Поля вставляются буквально: функция не добавляет backticks, не проверяет имена и не локализует их.
60
+
61
+ ## `createMissingContextError`
62
+
63
+ Возвращает обычный `Error` с тем же сообщением:
64
+
65
+ ```ts
66
+ const error = createMissingContextError({
67
+ hookName: "useDemo",
68
+ providerName: "DemoProvider"
69
+ });
70
+
71
+ console.log(error.name); // "Error".
72
+ console.log(error.message); // Подробное русское сообщение.
73
+ ```
74
+
75
+ Функция сама не делает `throw`:
76
+
77
+ ```ts
78
+ throw createMissingContextError(options);
79
+ ```
80
+
81
+ Stack trace создаётся стандартным конструктором `Error` в месте вызова.
82
+
83
+ ## Полный API
84
+
85
+ | Export | Назначение |
86
+ | ---------------------------------- | ---------------------------- |
87
+ | `createMissingContextErrorMessage` | Возвращает только строку |
88
+ | `createMissingContextError` | Возвращает экземпляр `Error` |
89
+
90
+ ## Типичные ошибки
91
+
92
+ - Возвращать fallback-object после ошибки и скрывать неправильное дерево Provider.
93
+ - Выбрасывать ошибку прямо в Provider вместо hook, который обнаружил `null`.
94
+ - Передавать пустые имена: helper не валидирует строки.
95
+ - Использовать этот модуль для сетевых/validation ошибок: он предназначен только для missing context contract.