@koslibs/components 0.0.0-stage → 1.0.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 ADDED
@@ -0,0 +1,27 @@
1
+ # @koslibs/components
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#1](https://github.com/koslibs/components/pull/1) [`8954b64`](https://github.com/koslibs/components/commit/8954b64becb19165000479731114463317438bcd) Thanks [@holypower777](https://github.com/holypower777)! - Первый стабильный выпуск 1.0.0 с Button и Spinner, перенесёнными из core-components GullEye.
8
+
9
+ Компонент доступен через `import { Button } from '@koslibs/components/Button'` вместе с типом ButtonProps. Поддерживает шесть вариантов оформления, размеры 32–72, формы rectangular и rounded, левый и правый аддоны, подпись, полную ширину, disabled и loading. Сохранены ref, стандартные HTML-атрибуты и минимальное время отображения загрузки 500 мс.
10
+
11
+ Spinner доступен отдельно через `import { Spinner } from '@koslibs/components/Spinner'` вместе с типом SpinnerProps. Поддерживает размеры 16, 24 и 48, управление видимостью, настройку цвета через style.color, className, id и dataTestId. Button использует этот же публичный Spinner для отображения загрузки.
12
+
13
+ CSS Modules подключаются вместе с используемыми компонентами. Добавлены значения по умолчанию для CSS-переменных размеров, скруглений и шрифтов, чтобы компоненты работали без глобальных стилей GullEye. Отдельный импорт Spinner не включает Button и его стили. Для обоих компонентов в Storybook доступны Docs и Playground.
14
+
15
+ Docs каждого компонента разделены на вкладки «Документация», «Разработчику» и «Обновления». Во вкладке разработчика доступны публичный импорт, Props и CSS-переменные из исходных стилей; обновления отображают CHANGELOG конкретного компонента.
16
+
17
+ Проверка отдельных импортов разделена на общий fixture и декларативные сценарии: JS и типы всех публичных компонентов проверяются автоматически, библиотека собирается один раз, а выбранные production-потребители — одним запуском builder в независимых environments с общей установленной копией пакета и отдельными выходными папками.
18
+
19
+ ## 0.1.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [`ee39a5b`](https://github.com/koslibs/components/commit/ee39a5bb32f94a3a76e4367ec6b14d9cbe4d41b2) Thanks [@holypower777](https://github.com/holypower777)! - Подготовлен каркас @koslibs/components для React 18/19 и TypeScript: сборка ESM, деклараций и CSS Modules через @koslibs/builder@1.0.0, общие настройки и release-инструменты @koslibs/configs@1.0.0, Storybook и workflow деплоя на GitHub Pages.
24
+
25
+ Настроены отдельные импорты вида @koslibs/components/Button. Тест production-сборки проверяет, что в бандл потребителя входят выбранный компонент и его CSS, а независимые соседние компоненты исключаются. Добавлены CI, Changesets и документация по переносу компонентов и ведению их историй изменений. Runtime-компоненты будут перенесены отдельно.
26
+
27
+ Опубликованных версий пока нет. После первого релиза Changesets добавит сюда историю пакета.
package/README.md CHANGED
@@ -1,3 +1,64 @@
1
- # Temporary Holding Version
1
+ Демо: <https://koslibs.github.io/components/>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ## Добавление компонента
4
+
5
+ Рекомендуемая структура:
6
+
7
+ ```text
8
+ src/components/Button/
9
+ index.ts
10
+ button.tsx
11
+ button.module.css
12
+ button.stories.tsx
13
+ button.docs.tsx
14
+ CHANGELOG.md
15
+ ```
16
+
17
+ 1. Перенесите компонент в `src/components/<Name>`; имя папки — PascalCase, например `Button`.
18
+ Регистр пути важен, в том числе на Linux. Стили храните в CSS Modules.
19
+ 2. Экспортируйте публичный API из его `index.ts`. Для удобного импорта из корня можно
20
+ также добавить реэкспорт в `src/index.ts`; отдельный импорт компонента его не использует.
21
+ 3. Общий шаблон в `package.json` → `exports` уже связывает каждый компонент с его файлами:
22
+
23
+ ```json
24
+ {
25
+ "./*": {
26
+ "types": "./dist/components/*/index.d.ts",
27
+ "import": "./dist/components/*/index.js"
28
+ }
29
+ }
30
+ ```
31
+
32
+ 4. Добавьте Docs, Playground и `CHANGELOG.md` по [примеру документации](https://github.com/koslibs/components/blob/main/docs/component-changelogs.md).
33
+ 5. Добавьте тесты поведения. `npm run test:unit` запускает unit-тесты через
34
+ `koslibs-builder lib:test`; `npm test` также проверяет отдельные импорты.
35
+ 6. Выполните сборку и проверьте реальные пути JS, деклараций и CSS в `dist`.
36
+
37
+ После публикации предполагается такой импорт:
38
+
39
+ ```tsx
40
+ import { Button } from '@koslibs/components/Button';
41
+ ```
42
+
43
+ Этот путь ведёт прямо к `dist/components/Button/index.js`, минуя общий `dist/index.js`.
44
+ Входной файл компонента должен экспортировать только свой API и импортировать только
45
+ нужные ему зависимости; не импортируйте внутри компонента общий barrel библиотеки.
46
+ Builder сохраняет отдельные JS-модули и отдельные CSS-файлы для каждого компонента.
47
+ Поэтому приложение подключает выбранный компонент, его стили и необходимые зависимости.
48
+ Соседние независимые компоненты не входят в его граф импортов.
49
+
50
+ При установке npm скачивает весь пакет; отдельные импорты ограничивают состав бандла
51
+ приложения. React, общие утилиты и другие зависимости используемого компонента
52
+ по-прежнему могут входить в бандл.
53
+
54
+ Тест `tests/component-imports.test.mjs` запускается через Node.js test runner командой
55
+ `npm test` (или отдельно `npm run test:imports`) и проверяет схему через builder:
56
+ собирает временную библиотеку с настоящим Button и независимым тестовым Spinner,
57
+ затем приложение с импортом только `@koslibs/components/Button`.
58
+ Проверка подтверждает наличие Button, его внутреннего спиннера загрузки и их CSS
59
+ и отсутствие независимого Spinner в production-бандле.
60
+ Временная библиотека находится в `.cache` и не публикуется.
61
+
62
+ Пакет выпускается в ESM. CSS помечен как side effect, чтобы оптимизация потребителя
63
+ не удаляла стили. Builder сохраняет структуру модулей; Docs, stories и Markdown
64
+ исключены из npm-сборки. Storybook загружает Markdown как текст через `?raw`.
@@ -0,0 +1,67 @@
1
+ import React, { type ButtonHTMLAttributes, type ReactNode } from 'react';
2
+ type ButtonOwnProps = {
3
+ /**
4
+ * Тип кнопки
5
+ * @default 'primary'
6
+ */
7
+ view?: 'primary' | 'secondary' | 'accent' | 'outlined' | 'transparent' | 'text';
8
+ /**
9
+ * Скругление кнопки
10
+ * @default 'rectangular'
11
+ */
12
+ shape?: 'rectangular' | 'rounded';
13
+ /**
14
+ * Размер кнопки
15
+ * @default 40
16
+ */
17
+ size?: 32 | 40 | 48 | 56 | 64 | 72;
18
+ /**
19
+ * Аддон слева от лейбла
20
+ */
21
+ leftAddon?: ReactNode;
22
+ /**
23
+ * Аддон справа от лейбла
24
+ */
25
+ rightAddon?: ReactNode;
26
+ /**
27
+ * Подпись под основным текстом
28
+ */
29
+ hint?: string;
30
+ /**
31
+ * Заставляет кнопку растянуться на ширину контейнера.
32
+ * @default false
33
+ */
34
+ block?: boolean;
35
+ /**
36
+ * Показать лоадер
37
+ * @default false
38
+ */
39
+ loading?: boolean;
40
+ /**
41
+ * Айди для автотестов.
42
+ */
43
+ dataTestId?: string;
44
+ /**
45
+ * Дополнительный класс
46
+ */
47
+ className?: string;
48
+ /**
49
+ * Дополнительный класс для label
50
+ */
51
+ labelClassName?: string;
52
+ /**
53
+ * Дополнительный класс для hint
54
+ */
55
+ hintClassName?: string;
56
+ /**
57
+ * Дополнительный класс для спиннера
58
+ */
59
+ spinnerClassName?: string;
60
+ /**
61
+ * Текст кнопки
62
+ */
63
+ children?: ReactNode;
64
+ };
65
+ export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & ButtonOwnProps;
66
+ export declare const Button: React.ForwardRefExoticComponent<React.ButtonHTMLAttributes<HTMLButtonElement> & ButtonOwnProps & React.RefAttributes<HTMLButtonElement>>;
67
+ export {};
@@ -0,0 +1,88 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import classnames from "classnames";
3
+ import react, { forwardRef, useEffect, useRef, useState } from "react";
4
+ import { Spinner } from "../Spinner/index.js";
5
+ import colors_module from "./colors.module.js";
6
+ import index_module from "./index.module.js";
7
+ const Button = /*#__PURE__*/ forwardRef(({ view = 'primary', shape = 'rectangular', size = 40, leftAddon, rightAddon, hint, block = false, loading = false, dataTestId, className, labelClassName, hintClassName, spinnerClassName, children, onClick, ...restProps }, ref)=>{
8
+ const [loaderTimePassed, setLoaderTimePassed] = useState(true);
9
+ const timerId = useRef(0);
10
+ const showLoader = loading || !loaderTimePassed;
11
+ const showHint = hint && [
12
+ 56,
13
+ 64,
14
+ 72
15
+ ].includes(size);
16
+ const { disabled, type = 'button', ...restButtonProps } = restProps;
17
+ const buttonChildren = /*#__PURE__*/ jsxs(react.Fragment, {
18
+ children: [
19
+ leftAddon && /*#__PURE__*/ jsx("span", {
20
+ className: index_module.addons,
21
+ children: leftAddon
22
+ }),
23
+ children && /*#__PURE__*/ jsxs("span", {
24
+ className: classnames(index_module.label, labelClassName, {
25
+ [index_module.stretchText]: !(leftAddon || rightAddon)
26
+ }),
27
+ children: [
28
+ children,
29
+ showHint && /*#__PURE__*/ jsx("span", {
30
+ className: classnames(index_module.hint, colors_module.hint, hintClassName),
31
+ children: hint
32
+ })
33
+ ]
34
+ }),
35
+ showLoader && /*#__PURE__*/ jsx(Spinner, {
36
+ size: 24,
37
+ dataTestId: dataTestId ? `${dataTestId}-loader` : void 0,
38
+ visible: true,
39
+ className: classnames(index_module.loader, colors_module.loader, spinnerClassName)
40
+ }),
41
+ rightAddon && /*#__PURE__*/ jsx("span", {
42
+ className: index_module.addons,
43
+ children: rightAddon
44
+ })
45
+ ]
46
+ });
47
+ useEffect(()=>{
48
+ if (loading) {
49
+ window.clearTimeout(timerId.current);
50
+ setLoaderTimePassed(false);
51
+ timerId.current = window.setTimeout(()=>{
52
+ setLoaderTimePassed(true);
53
+ }, 500);
54
+ }
55
+ }, [
56
+ loading
57
+ ]);
58
+ useEffect(()=>()=>{
59
+ window.clearTimeout(timerId.current);
60
+ }, []);
61
+ const handleClick = (e)=>{
62
+ if (disabled || showLoader) {
63
+ e.preventDefault();
64
+ e.stopPropagation();
65
+ return;
66
+ }
67
+ onClick?.(e);
68
+ };
69
+ return /*#__PURE__*/ jsx("button", {
70
+ ...restButtonProps,
71
+ className: classnames(index_module.component, index_module[view], index_module[`size-${size}`], index_module[shape], colors_module.component, colors_module[view], {
72
+ [index_module.block]: block,
73
+ [index_module.loading]: showLoader,
74
+ [index_module.withRightAddon]: Boolean(rightAddon),
75
+ [index_module.withLeftAddon]: Boolean(leftAddon),
76
+ [colors_module.loading]: showLoader
77
+ }, className),
78
+ "data-test-id": dataTestId,
79
+ type: type,
80
+ disabled: disabled || showLoader,
81
+ "aria-busy": showLoader,
82
+ onClick: handleClick,
83
+ ref: ref,
84
+ children: buttonChildren
85
+ });
86
+ });
87
+ Button.displayName = 'Button';
88
+ export { Button };
@@ -0,0 +1,14 @@
1
+ import "./colors_module.css";
2
+ const colors_module = {
3
+ primary: "primary-jF1frT",
4
+ secondary: "secondary-em6CEW",
5
+ accent: "accent-cr9M8g",
6
+ transparent: "transparent-osSlaN",
7
+ text: "text-fLOhKS",
8
+ loader: "loader-wrwZSN",
9
+ hint: "hint-JAXcv5",
10
+ outlined: "outlined-xzRrKO",
11
+ component: "component-GmKJmC",
12
+ loading: "loading-LzTYPE"
13
+ };
14
+ export default colors_module;
@@ -0,0 +1,164 @@
1
+ @import "./vars.css";
2
+
3
+ .primary-jF1frT, .secondary-em6CEW, .accent-cr9M8g, .transparent-osSlaN, .text-fLOhKS {
4
+ box-shadow: none;
5
+ }
6
+
7
+ .primary-jF1frT {
8
+ color: var(--button-inverse-color);
9
+ background-color: var(--button-primary-background-color);
10
+
11
+ @media (hover: hover) {
12
+ &:hover {
13
+ background-color: var(--button-primary-hover-background-color);
14
+ }
15
+ }
16
+
17
+ &:active {
18
+ background-color: var(--button-primary-active-background-color);
19
+ }
20
+
21
+ & > .loader-wrwZSN {
22
+ color: var(--button-inverse-color);
23
+ }
24
+
25
+ &:not([disabled]) .hint-JAXcv5 {
26
+ color: var(--button-inverse-hint-color);
27
+ }
28
+ }
29
+
30
+ .secondary-em6CEW {
31
+ color: var(--button-default-color);
32
+ background-color: var(--button-secondary-background-color);
33
+
34
+ @media (hover: hover) {
35
+ &:hover {
36
+ background-color: var(--button-secondary-hover-background-color);
37
+ }
38
+ }
39
+
40
+ &:active {
41
+ background-color: var(--button-secondary-active-background-color);
42
+ }
43
+
44
+ & > .loader-wrwZSN {
45
+ color: var(--button-default-color);
46
+ }
47
+
48
+ &:not([disabled]) .hint-JAXcv5 {
49
+ color: var(--button-hint-color);
50
+ }
51
+ }
52
+
53
+ .accent-cr9M8g {
54
+ color: var(--button-inverse-color);
55
+ background-color: var(--button-accent-background-color);
56
+
57
+ @media (hover: hover) {
58
+ &:hover {
59
+ background-color: var(--button-accent-hover-background-color);
60
+ }
61
+ }
62
+
63
+ &:active {
64
+ background-color: var(--button-accent-active-background-color);
65
+ }
66
+
67
+ & > .loader-wrwZSN {
68
+ color: var(--button-inverse-color);
69
+ }
70
+
71
+ &:not([disabled]) .hint-JAXcv5 {
72
+ color: var(--button-inverse-hint-color);
73
+ }
74
+ }
75
+
76
+ .outlined-xzRrKO {
77
+ color: var(--button-default-color);
78
+ box-shadow: inset 0 0 0 1px var(--button-primary-background-color);
79
+ background: none;
80
+
81
+ @media (hover: hover) {
82
+ &:hover {
83
+ background-color: var(--button-subtle-hover-background-color);
84
+ box-shadow: inset 0 0 0 1px var(--button-primary-hover-background-color);
85
+ }
86
+ }
87
+
88
+ &:active {
89
+ background-color: var(--button-subtle-active-background-color);
90
+ box-shadow: inset 0 0 0 1px var(--button-primary-active-background-color);
91
+ }
92
+
93
+ & > .loader-wrwZSN {
94
+ color: var(--button-default-color);
95
+ }
96
+
97
+ &:not([disabled]) .hint-JAXcv5 {
98
+ color: var(--button-hint-color);
99
+ }
100
+ }
101
+
102
+ .transparent-osSlaN {
103
+ color: var(--button-default-color);
104
+ background-color: var(--button-transparent-background-color);
105
+
106
+ @media (hover: hover) {
107
+ &:hover {
108
+ background-color: var(--button-subtle-hover-background-color);
109
+ }
110
+ }
111
+
112
+ &:active {
113
+ background-color: var(--button-subtle-active-background-color);
114
+ }
115
+
116
+ & > .loader-wrwZSN {
117
+ color: var(--button-default-color);
118
+ }
119
+
120
+ &:not([disabled]) .hint-JAXcv5 {
121
+ color: var(--button-hint-color);
122
+ }
123
+ }
124
+
125
+ .text-fLOhKS {
126
+ background-color: var(--button-transparent-background-color);
127
+ color: var(--button-default-color);
128
+
129
+ @media (hover: hover) {
130
+ &:hover {
131
+ color: var(--button-text-hover-color);
132
+ }
133
+ }
134
+
135
+ &:active {
136
+ color: var(--button-text-active-color);
137
+ }
138
+
139
+ & > .loader-wrwZSN {
140
+ color: var(--button-default-color);
141
+ }
142
+
143
+ &:not([disabled]) .hint-JAXcv5 {
144
+ color: var(--button-hint-color);
145
+ }
146
+ }
147
+
148
+ .component-GmKJmC[disabled] {
149
+ &:not(.loading-LzTYPE) {
150
+ background-color: var(--button-disabled-background-color);
151
+ color: var(--button-disabled-color);
152
+ box-shadow: none;
153
+
154
+ &.hint-JAXcv5 {
155
+ color: var(--button-disabled-hint-color);
156
+ }
157
+ }
158
+
159
+ &.transparent-osSlaN, &.text-fLOhKS {
160
+ background-color: var(--button-transparent-background-color);
161
+ color: var(--button-disabled-color);
162
+ }
163
+ }
164
+
@@ -0,0 +1,2 @@
1
+ export { Button } from './button.js';
2
+ export type { ButtonProps } from './button.js';
@@ -0,0 +1 @@
1
+ export { Button } from "./button.js";
@@ -0,0 +1,30 @@
1
+ import "./index_module.css";
2
+ const index_module = {
3
+ component: "component-vxc0gC",
4
+ "size-32": "size-32-oD8MDp",
5
+ size32: "size-32-oD8MDp",
6
+ "size-40": "size-40-Pu2_h6",
7
+ size40: "size-40-Pu2_h6",
8
+ "size-48": "size-48-U3XIYx",
9
+ size48: "size-48-U3XIYx",
10
+ "size-56": "size-56-P_94sz",
11
+ size56: "size-56-P_94sz",
12
+ "size-64": "size-64-ZlQLt3",
13
+ size64: "size-64-ZlQLt3",
14
+ "size-72": "size-72-zkSrcs",
15
+ size72: "size-72-zkSrcs",
16
+ withRightAddon: "withRightAddon-TNcFTm",
17
+ text: "text-Ifdq0q",
18
+ withLeftAddon: "withLeftAddon-N3YDHw",
19
+ addons: "addons-NxGsiY",
20
+ block: "block-xREK1A",
21
+ rectangular: "rectangular-PFbww7",
22
+ rounded: "rounded-kQqesg",
23
+ loading: "loading-DPEeSl",
24
+ label: "label-gwmTIK",
25
+ stretchText: "stretchText-Nm2RYn",
26
+ hint: "hint-AFu_zz",
27
+ loader: "loader-BRksFP",
28
+ transparent: "transparent-w7vhL3"
29
+ };
30
+ export default index_module;
@@ -0,0 +1,242 @@
1
+ @import "./vars.css";
2
+
3
+ .component-vxc0gC {
4
+ box-sizing: border-box;
5
+ vertical-align: middle;
6
+ width: fit-content;
7
+ font-family: var(--font-ubuntu, "Ubuntu", sans-serif);
8
+ font-size: var(--size-16, 16px);
9
+ text-align: center;
10
+ margin: var(--size-0, 0px);
11
+ padding: var(--size-0, 0px) var(--size-24, 24px);
12
+ cursor: pointer;
13
+ -webkit-user-select: none;
14
+ user-select: none;
15
+ border: none;
16
+ outline: none;
17
+ flex-flow: row;
18
+ justify-content: center;
19
+ align-items: center;
20
+ text-decoration: none;
21
+ transition: background-color .1s ease-in-out, color .1s ease-in-out, box-shadow .1s ease-in-out;
22
+ display: inline-flex;
23
+ position: relative;
24
+ }
25
+
26
+ .size-32-oD8MDp, .size-40-Pu2_h6 {
27
+ font-size: 14px;
28
+ font-weight: 500;
29
+ line-height: 20px;
30
+ font-family: var(--font-family-system, system-ui, sans-serif);
31
+ }
32
+
33
+ .size-32-oD8MDp {
34
+ min-width: 80px;
35
+ min-height: 32px;
36
+ padding: var(--size-0, 0px) var(--size-16, 16px);
37
+ }
38
+
39
+ .size-40-Pu2_h6 {
40
+ min-width: 88px;
41
+ min-height: 40px;
42
+ padding: var(--size-0, 0px) var(--size-20, 20px);
43
+ }
44
+
45
+ .size-48-U3XIYx, .size-56-P_94sz {
46
+ font-size: 16px;
47
+ font-weight: 500;
48
+ line-height: 20px;
49
+ font-family: var(--font-family-system, system-ui, sans-serif);
50
+ }
51
+
52
+ .size-48-U3XIYx {
53
+ min-width: 104px;
54
+ min-height: 48px;
55
+ padding: var(--size-0, 0px) var(--size-24, 24px);
56
+ }
57
+
58
+ .size-56-P_94sz {
59
+ min-width: 128px;
60
+ min-height: 56px;
61
+ padding: var(--size-0, 0px) var(--size-28, 28px);
62
+ }
63
+
64
+ .size-64-ZlQLt3, .size-72-zkSrcs {
65
+ font-size: 18px;
66
+ font-weight: 500;
67
+ line-height: 24px;
68
+ font-family: var(--font-family-system, system-ui, sans-serif);
69
+ }
70
+
71
+ .size-64-ZlQLt3 {
72
+ min-width: 160px;
73
+ min-height: 64px;
74
+ padding: var(--size-0, 0px) var(--size-32, 32px);
75
+ }
76
+
77
+ .size-72-zkSrcs {
78
+ min-width: 168px;
79
+ min-height: 72px;
80
+ padding: var(--size-0, 0px) var(--size-36, 36px);
81
+ }
82
+
83
+ .withRightAddon-TNcFTm {
84
+ &.size-32-oD8MDp {
85
+ padding-right: var(--size-12, 12px);
86
+ }
87
+
88
+ &.size-40-Pu2_h6 {
89
+ padding-right: var(--size-16, 16px);
90
+ }
91
+
92
+ &.size-48-U3XIYx {
93
+ padding-right: var(--size-20, 20px);
94
+ }
95
+
96
+ &.size-56-P_94sz {
97
+ padding-right: var(--size-24, 24px);
98
+ }
99
+
100
+ &.size-64-ZlQLt3 {
101
+ padding-right: var(--size-28, 28px);
102
+ }
103
+
104
+ &.size-72-zkSrcs {
105
+ padding-right: var(--size-32, 32px);
106
+ }
107
+
108
+ &.text-Ifdq0q {
109
+ padding-right: var(--size-0, 0px);
110
+ }
111
+ }
112
+
113
+ .withLeftAddon-N3YDHw {
114
+ &.size-32-oD8MDp {
115
+ padding-left: var(--size-12, 12px);
116
+ }
117
+
118
+ &.size-40-Pu2_h6 {
119
+ padding-left: var(--size-16, 16px);
120
+ }
121
+
122
+ &.size-48-U3XIYx {
123
+ padding-left: var(--size-20, 20px);
124
+ }
125
+
126
+ &.size-56-P_94sz {
127
+ padding-left: var(--size-24, 24px);
128
+ }
129
+
130
+ &.size-64-ZlQLt3 {
131
+ padding-left: var(--size-28, 28px);
132
+ }
133
+
134
+ &.size-72-zkSrcs {
135
+ padding-left: var(--size-32, 32px);
136
+ }
137
+
138
+ &.text-Ifdq0q {
139
+ padding-left: var(--size-0, 0px);
140
+ }
141
+ }
142
+
143
+ .text-Ifdq0q {
144
+ min-width: 0;
145
+ min-height: 0;
146
+ padding: var(--size-0, 0px);
147
+ appearance: none;
148
+ border: none;
149
+
150
+ &.size-48-U3XIYx, &.size-56-P_94sz {
151
+ min-height: 24px;
152
+ }
153
+ }
154
+
155
+ .size-48-U3XIYx, .size-56-P_94sz {
156
+ & .addons-NxGsiY:first-child {
157
+ margin-right: var(--size-8, 8px);
158
+ }
159
+
160
+ & .addons-NxGsiY:last-child {
161
+ margin-left: var(--size-8, 8px);
162
+ }
163
+
164
+ & .addons-NxGsiY:only-child {
165
+ margin: var(--size-0, 0px);
166
+ }
167
+ }
168
+
169
+ .size-64-ZlQLt3, .size-72-zkSrcs {
170
+ & .addons-NxGsiY:first-child {
171
+ margin-right: var(--size-12, 12px);
172
+ }
173
+
174
+ & .addons-NxGsiY:last-child {
175
+ margin-left: var(--size-12, 12px);
176
+ }
177
+
178
+ & .addons-NxGsiY:only-child {
179
+ margin: var(--size-0, 0px);
180
+ }
181
+ }
182
+
183
+ .block-xREK1A {
184
+ width: 100%;
185
+ display: flex;
186
+ }
187
+
188
+ .rectangular-PFbww7 {
189
+ border-radius: var(--border-radius-8, 8px);
190
+ }
191
+
192
+ .rounded-kQqesg {
193
+ border-radius: var(--border-radius-pill, 99px);
194
+ }
195
+
196
+ .addons-NxGsiY {
197
+ margin: var(--size-0, 0px);
198
+ flex-shrink: 0;
199
+ align-items: center;
200
+ display: flex;
201
+
202
+ &:first-child {
203
+ margin-right: var(--size-6, 6px);
204
+ }
205
+
206
+ &:last-child {
207
+ margin-left: var(--size-6, 6px);
208
+ }
209
+
210
+ &:only-child {
211
+ margin: var(--size-0, 0px);
212
+ }
213
+ }
214
+
215
+ .loading-DPEeSl .label-gwmTIK, .loading-DPEeSl .addons-NxGsiY {
216
+ opacity: 0;
217
+ }
218
+
219
+ .stretchText-Nm2RYn {
220
+ flex-grow: 1;
221
+ }
222
+
223
+ .hint-AFu_zz {
224
+ font-size: 14px;
225
+ font-weight: 400;
226
+ line-height: 18px;
227
+ font-family: var(--font-primary, "Inter", sans-serif);
228
+ color: var(--button-hint-color);
229
+ display: block;
230
+ }
231
+
232
+ .loader-BRksFP {
233
+ left: var(--size-0, 0px);
234
+ right: var(--size-0, 0px);
235
+ margin: var(--size-0, 0px) auto;
236
+ position: absolute;
237
+ }
238
+
239
+ .transparent-w7vhL3 {
240
+ min-width: 0;
241
+ }
242
+
@@ -0,0 +1,24 @@
1
+ :root {
2
+ --button-default-color: #1c1a18;
3
+ --button-inverse-color: #f5f3ec;
4
+ --button-hint-color: #1c1a1862;
5
+ --button-inverse-hint-color: #f5f3ec72;
6
+ --button-primary-background-color: #ff6b0a;
7
+ --button-primary-hover-background-color: #f05f00;
8
+ --button-primary-active-background-color: #d95300;
9
+ --button-secondary-background-color: #e0dbce;
10
+ --button-secondary-hover-background-color: #d5cdbc;
11
+ --button-secondary-active-background-color: #c8beaa;
12
+ --button-accent-background-color: #7967f2;
13
+ --button-accent-hover-background-color: #6c5ae6;
14
+ --button-accent-active-background-color: #5e4cd8;
15
+ --button-transparent-background-color: transparent;
16
+ --button-subtle-hover-background-color: #f2f3ea;
17
+ --button-subtle-active-background-color: #e5e7db;
18
+ --button-text-hover-color: #4a463f;
19
+ --button-text-active-color: #6a645a;
20
+ --button-disabled-background-color: #cfd1cb;
21
+ --button-disabled-color: #858981;
22
+ --button-disabled-hint-color: #9da099;
23
+ }
24
+
@@ -0,0 +1,5 @@
1
+ import "./colors_module.css";
2
+ const colors_module = {
3
+ component: "component-xRdHbz"
4
+ };
5
+ export default colors_module;
@@ -0,0 +1,6 @@
1
+ @import "./vars.css";
2
+
3
+ .component-xRdHbz {
4
+ color: var(--spinner-default-color);
5
+ }
6
+
@@ -0,0 +1,2 @@
1
+ export { Spinner } from './spinner.js';
2
+ export type { SpinnerProps } from './spinner.js';
@@ -0,0 +1 @@
1
+ export { Spinner } from "./spinner.js";
@@ -0,0 +1,9 @@
1
+ import "./index_module.css";
2
+ const index_module = {
3
+ spinner: "spinner-KHk8mw",
4
+ spin_animation: "spin_animation-pow2iy",
5
+ spinAnimation: "spin_animation-pow2iy",
6
+ visible: "visible-wk34JZ",
7
+ gradient: "gradient-QKWB8G"
8
+ };
9
+ export default index_module;
@@ -0,0 +1,29 @@
1
+ @import "./vars.css";
2
+
3
+ .spinner-KHk8mw {
4
+ box-sizing: content-box;
5
+ animation: spin_animation-pow2iy var(--spinner-animation-duration) infinite
6
+ var(--spinner-animation-timing-function);
7
+ display: none;
8
+ }
9
+
10
+ .visible-wk34JZ {
11
+ display: var(--spinner-display-visible);
12
+ vertical-align: middle;
13
+ }
14
+
15
+ .gradient-QKWB8G {
16
+ width: 100%;
17
+ height: 100%;
18
+ }
19
+
20
+ @keyframes spin_animation-pow2iy {
21
+ from {
22
+ transform: rotate(0);
23
+ }
24
+
25
+ to {
26
+ transform: rotate(360deg);
27
+ }
28
+ }
29
+
@@ -0,0 +1,7 @@
1
+ import "./preset_module.css";
2
+ const preset_module = {
3
+ preset16: "preset16-UI50gd",
4
+ preset24: "preset24-PPOWk6",
5
+ preset48: "preset48-Py1Pzz"
6
+ };
7
+ export default preset_module;
@@ -0,0 +1,12 @@
1
+ .preset16-UI50gd {
2
+ padding: 1px;
3
+ }
4
+
5
+ .preset24-PPOWk6 {
6
+ padding: var(--size-2, 2px);
7
+ }
8
+
9
+ .preset48-Py1Pzz {
10
+ padding: var(--size-4, 4px);
11
+ }
12
+
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ export type SpinnerProps = {
3
+ /**
4
+ * Управление видимостью компонента
5
+ * @default false
6
+ */
7
+ visible?: boolean;
8
+ /**
9
+ * Размер спиннера
10
+ *
11
+ * @default 24
12
+ */
13
+ size?: 16 | 24 | 48;
14
+ /**
15
+ * Дополнительный класс
16
+ */
17
+ className?: string;
18
+ /**
19
+ * Идентификатор компонента в DOM
20
+ */
21
+ id?: string;
22
+ /**
23
+ * Идентификатор для систем автоматизированного тестирования
24
+ */
25
+ dataTestId?: string;
26
+ /**
27
+ * Дополнительные инлайн стили для cпиннера
28
+ */
29
+ style?: React.CSSProperties;
30
+ };
31
+ export declare const Spinner: ({ visible, size, className, id, dataTestId, style }: SpinnerProps) => React.JSX.Element;
@@ -0,0 +1,80 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import classnames from "classnames";
3
+ import { useId } from "react";
4
+ import colors_module from "./colors.module.js";
5
+ import index_module from "./index.module.js";
6
+ import preset_module from "./preset.module.js";
7
+ const PRESET_CONFIG = {
8
+ 16: [
9
+ 2,
10
+ 14,
11
+ 'preset16'
12
+ ],
13
+ 24: [
14
+ 2,
15
+ 20,
16
+ 'preset24'
17
+ ],
18
+ 48: [
19
+ 4,
20
+ 40,
21
+ 'preset48'
22
+ ]
23
+ };
24
+ const Spinner = ({ visible, size = 24, className, id, dataTestId, style })=>{
25
+ const [lineWidth, presetSize, styleKey] = PRESET_CONFIG[size];
26
+ const presetClassname = preset_module[styleKey];
27
+ const uniqId = useId();
28
+ const radius = presetSize / 2 - lineWidth / 2;
29
+ const rotationAngle = Math.ceil(180 * Math.asin(lineWidth / 2 / radius) / Math.PI);
30
+ const gap = 90;
31
+ const pathLength = 360;
32
+ const strokeDasharray = `${pathLength - gap - rotationAngle} ${gap + rotationAngle}`;
33
+ const gradient = `conic-gradient(from ${rotationAngle}deg, transparent ${gap - 2 * rotationAngle}deg, currentColor)`;
34
+ return /*#__PURE__*/ jsxs("svg", {
35
+ "aria-hidden": "true",
36
+ viewBox: `0 0 ${presetSize} ${presetSize}`,
37
+ style: {
38
+ ...style,
39
+ height: presetSize,
40
+ width: presetSize
41
+ },
42
+ className: classnames(index_module.spinner, colors_module.component, presetClassname, className, {
43
+ [index_module.visible]: visible
44
+ }),
45
+ "data-test-id": dataTestId,
46
+ id: id,
47
+ children: [
48
+ /*#__PURE__*/ jsx("defs", {
49
+ children: /*#__PURE__*/ jsx("mask", {
50
+ id: uniqId,
51
+ children: /*#__PURE__*/ jsx("circle", {
52
+ cx: "50%",
53
+ cy: "50%",
54
+ r: radius,
55
+ strokeWidth: lineWidth,
56
+ strokeLinecap: "round",
57
+ stroke: "#fff",
58
+ strokeDashoffset: -rotationAngle,
59
+ strokeDasharray: strokeDasharray,
60
+ pathLength: pathLength
61
+ })
62
+ })
63
+ }),
64
+ /*#__PURE__*/ jsx("foreignObject", {
65
+ x: "0",
66
+ y: "0",
67
+ width: presetSize,
68
+ height: presetSize,
69
+ mask: `url(#${uniqId})`,
70
+ children: /*#__PURE__*/ jsx("div", {
71
+ className: index_module.gradient,
72
+ style: {
73
+ backgroundImage: gradient
74
+ }
75
+ })
76
+ })
77
+ ]
78
+ });
79
+ };
80
+ export { Spinner };
@@ -0,0 +1,7 @@
1
+ :root {
2
+ --spinner-display-visible: inline-block;
3
+ --spinner-animation-duration: .8s;
4
+ --spinner-animation-timing-function: linear;
5
+ --spinner-default-color: #030306e0;
6
+ }
7
+
@@ -0,0 +1,4 @@
1
+ export { Button } from './components/Button/index.js';
2
+ export type { ButtonProps } from './components/Button/index.js';
3
+ export { Spinner } from './components/Spinner/index.js';
4
+ export type { SpinnerProps } from './components/Spinner/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { Button } from "./components/Button/index.js";
2
+ export { Spinner } from "./components/Spinner/index.js";
package/package.json CHANGED
@@ -1,6 +1,84 @@
1
1
  {
2
- "name": "@koslibs/components",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@koslibs/components",
3
+ "version": "1.0.0",
4
+ "description": "Shared React components for koslibs projects",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./*": {
14
+ "types": "./dist/components/*/index.d.ts",
15
+ "import": "./dist/components/*/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "CHANGELOG.md"
23
+ ],
24
+ "sideEffects": [
25
+ "**/*.css"
26
+ ],
27
+ "engines": {
28
+ "node": ">=24.13.0"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/koslibs/components.git"
33
+ },
34
+ "homepage": "https://koslibs.github.io/components/",
35
+ "bugs": {
36
+ "url": "https://github.com/koslibs/components/issues"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "start": "koslibs-builder lib:start",
43
+ "watch": "koslibs-builder lib:watch",
44
+ "build": "koslibs-builder lib:build",
45
+ "typecheck": "koslibs-builder lib:typecheck",
46
+ "lint": "koslibs-lint lint",
47
+ "lint:fix": "koslibs-lint lint:fix",
48
+ "format": "koslibs-lint format",
49
+ "format:check": "koslibs-lint format:check",
50
+ "storybook": "koslibs-builder storybook:start",
51
+ "build-storybook": "koslibs-builder storybook:build",
52
+ "test": "npm run test:unit && npm run test:imports",
53
+ "test:unit": "koslibs-builder lib:test",
54
+ "test:imports": "node --test tests/component-imports.test.mjs",
55
+ "prepack": "koslibs-builder lib:build",
56
+ "changeset": "koslibs-release add",
57
+ "changeset:check": "koslibs-release check",
58
+ "version-package": "koslibs-release version",
59
+ "release": "koslibs-release publish",
60
+ "prepare": "koslibs-release hooks:install"
61
+ },
62
+ "prettier": "@koslibs/configs/prettier",
63
+ "commitlint": {
64
+ "extends": [
65
+ "@koslibs/configs/commitlint"
66
+ ]
67
+ },
68
+ "devDependencies": {
69
+ "@koslibs/builder": "^1.0.0",
70
+ "@koslibs/configs": "^1.0.0",
71
+ "@types/node": "^24.19.0",
72
+ "@types/react": "^18.3.18",
73
+ "@types/react-dom": "^18.3.5",
74
+ "react": "18.3.1",
75
+ "react-dom": "18.3.1"
76
+ },
77
+ "peerDependencies": {
78
+ "react": "^18.3.1 || ^19.0.0",
79
+ "react-dom": "^18.3.1 || ^19.0.0"
80
+ },
81
+ "dependencies": {
82
+ "classnames": "2.5.1"
83
+ }
84
+ }