@dmitriikapustin/ui 0.2.13 → 0.3.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.
package/dist/index.cjs CHANGED
@@ -194,6 +194,31 @@ __styleInject(`@charset "UTF-8";
194
194
  background: var(--border-color);
195
195
  }
196
196
 
197
+ .Button-module_danger {
198
+ background: var(--color-error);
199
+ border: 1px solid var(--color-error);
200
+ color: #ffffff;
201
+ box-shadow: var(--shadow-sm);
202
+ }
203
+ .Button-module_danger:hover:not(:disabled) {
204
+ background: var(--color-error-dark);
205
+ border-color: var(--color-error-dark);
206
+ box-shadow: var(--shadow-md);
207
+ }
208
+ .Button-module_danger:active:not(:disabled) {
209
+ background: var(--color-error-dark);
210
+ border-color: var(--color-error-dark);
211
+ }
212
+ .Button-module_danger:focus-visible {
213
+ outline: 2px solid var(--color-error-light);
214
+ outline-offset: 2px;
215
+ }
216
+ .Button-module_danger:disabled {
217
+ background: var(--color-error-light);
218
+ border-color: var(--color-error-light);
219
+ color: rgba(255, 255, 255, 0.7);
220
+ }
221
+
197
222
  /* \u2500\u2500\u2500 Sizes \u2500\u2500\u2500 */
198
223
  .Button-module_sm {
199
224
  font-size: 13px;
@@ -228,9 +253,9 @@ __styleInject(`@charset "UTF-8";
228
253
  cursor: not-allowed;
229
254
  pointer-events: none;
230
255
  }`);
231
- var __default2 = { "root": "Button-module_root", "primary": "Button-module_primary", "ghost": "Button-module_ghost", "secondary": "Button-module_secondary", "outline": "Button-module_outline", "sm": "Button-module_sm", "md": "Button-module_md", "hero": "Button-module_hero", "disabled": "Button-module_disabled" };
232
- function Button(_a) {
233
- var _b = _a, {
256
+ var __default2 = { "root": "Button-module_root", "primary": "Button-module_primary", "ghost": "Button-module_ghost", "secondary": "Button-module_secondary", "outline": "Button-module_outline", "danger": "Button-module_danger", "sm": "Button-module_sm", "md": "Button-module_md", "hero": "Button-module_hero", "disabled": "Button-module_disabled" };
257
+ function Button(props) {
258
+ const {
234
259
  variant = "primary",
235
260
  size = "md",
236
261
  loading = false,
@@ -239,7 +264,70 @@ function Button(_a) {
239
264
  children,
240
265
  disabled,
241
266
  className = ""
242
- } = _b, props = __objRest(_b, [
267
+ } = props;
268
+ const rootClass = `${__default2.root} ${__default2[variant]} ${__default2[size]}${disabled || loading ? ` ${__default2.disabled}` : ""}${className ? ` ${className}` : ""}`;
269
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
270
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm" }) : icon,
271
+ children,
272
+ iconRight
273
+ ] });
274
+ if (props.href !== void 0) {
275
+ const _a = props, {
276
+ variant: _v2,
277
+ size: _s2,
278
+ loading: _l2,
279
+ icon: _i2,
280
+ iconRight: _ir2,
281
+ children: _c2,
282
+ disabled: _d2,
283
+ className: _cn2,
284
+ href: href,
285
+ target,
286
+ rel,
287
+ onClick
288
+ } = _a, domRest2 = __objRest(_a, [
289
+ // strip internal/non-DOM props
290
+ "variant",
291
+ "size",
292
+ "loading",
293
+ "icon",
294
+ "iconRight",
295
+ "children",
296
+ "disabled",
297
+ "className",
298
+ // pull out anchor-specific that we set explicitly below
299
+ "href",
300
+ "target",
301
+ "rel",
302
+ "onClick"
303
+ ]);
304
+ const safeRel = target === "_blank" ? rel != null ? rel : "noopener noreferrer" : rel;
305
+ const handleClick = disabled || loading ? (e) => e.preventDefault() : onClick;
306
+ return /* @__PURE__ */ jsxRuntime.jsx(
307
+ "a",
308
+ __spreadProps(__spreadValues({}, domRest2), {
309
+ href,
310
+ target,
311
+ rel: safeRel,
312
+ className: rootClass,
313
+ "aria-disabled": disabled || loading || void 0,
314
+ tabIndex: disabled || loading ? -1 : domRest2.tabIndex,
315
+ onClick: handleClick,
316
+ children: content
317
+ })
318
+ );
319
+ }
320
+ const _b = props, {
321
+ variant: _v,
322
+ size: _s,
323
+ loading: _l,
324
+ icon: _i,
325
+ iconRight: _ir,
326
+ children: _c,
327
+ disabled: _d,
328
+ className: _cn,
329
+ href: _href
330
+ } = _b, domRest = __objRest(_b, [
243
331
  "variant",
244
332
  "size",
245
333
  "loading",
@@ -247,19 +335,15 @@ function Button(_a) {
247
335
  "iconRight",
248
336
  "children",
249
337
  "disabled",
250
- "className"
338
+ "className",
339
+ "href"
251
340
  ]);
252
- return /* @__PURE__ */ jsxRuntime.jsxs(
341
+ return /* @__PURE__ */ jsxRuntime.jsx(
253
342
  "button",
254
- __spreadProps(__spreadValues({
255
- className: `${__default2.root} ${__default2[variant]} ${__default2[size]}${disabled || loading ? ` ${__default2.disabled}` : ""}${className ? ` ${className}` : ""}`,
256
- disabled: disabled || loading
257
- }, props), {
258
- children: [
259
- loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm" }) : icon,
260
- children,
261
- iconRight
262
- ]
343
+ __spreadProps(__spreadValues({}, domRest), {
344
+ className: rootClass,
345
+ disabled: disabled || loading,
346
+ children: content
263
347
  })
264
348
  );
265
349
  }
@@ -900,15 +984,64 @@ __styleInject(`@charset "UTF-8";
900
984
  cursor: not-allowed;
901
985
  }`);
902
986
  var __default10 = { "root": "IconButton-module_root", "primary": "IconButton-module_primary", "secondary": "IconButton-module_secondary", "clear": "IconButton-module_clear", "disabled": "IconButton-module_disabled", "contrast": "IconButton-module_contrast", "isDisabled": "IconButton-module_isDisabled" };
903
- function IconButton(_a) {
904
- var _b = _a, { icon, variant = "primary", className = "", disabled } = _b, props = __objRest(_b, ["icon", "variant", "className", "disabled"]);
987
+ function IconButton(props) {
988
+ const { icon, variant = "primary", className = "", disabled } = props;
905
989
  const isDisabled = variant === "disabled" || disabled;
990
+ const rootClass = `${__default10.root} ${__default10[variant]}${isDisabled ? ` ${__default10.isDisabled}` : ""}${className ? ` ${className}` : ""}`;
991
+ if (props.href !== void 0) {
992
+ const _a = props, {
993
+ icon: _i2,
994
+ variant: _v2,
995
+ className: _cn2,
996
+ disabled: _d2,
997
+ href,
998
+ target,
999
+ rel,
1000
+ onClick
1001
+ } = _a, domRest2 = __objRest(_a, [
1002
+ "icon",
1003
+ "variant",
1004
+ "className",
1005
+ "disabled",
1006
+ "href",
1007
+ "target",
1008
+ "rel",
1009
+ "onClick"
1010
+ ]);
1011
+ const safeRel = target === "_blank" ? rel != null ? rel : "noopener noreferrer" : rel;
1012
+ const handleClick = isDisabled ? (e) => e.preventDefault() : onClick;
1013
+ return /* @__PURE__ */ jsxRuntime.jsx(
1014
+ "a",
1015
+ __spreadProps(__spreadValues({}, domRest2), {
1016
+ href,
1017
+ target,
1018
+ rel: safeRel,
1019
+ className: rootClass,
1020
+ "aria-disabled": isDisabled || void 0,
1021
+ tabIndex: isDisabled ? -1 : domRest2.tabIndex,
1022
+ onClick: handleClick,
1023
+ children: icon
1024
+ })
1025
+ );
1026
+ }
1027
+ const _b = props, {
1028
+ icon: _i,
1029
+ variant: _v,
1030
+ className: _cn,
1031
+ disabled: _d,
1032
+ href: _href
1033
+ } = _b, domRest = __objRest(_b, [
1034
+ "icon",
1035
+ "variant",
1036
+ "className",
1037
+ "disabled",
1038
+ "href"
1039
+ ]);
906
1040
  return /* @__PURE__ */ jsxRuntime.jsx(
907
1041
  "button",
908
- __spreadProps(__spreadValues({
909
- className: `${__default10.root} ${__default10[variant]}${isDisabled ? ` ${__default10.isDisabled}` : ""}${className ? ` ${className}` : ""}`,
910
- disabled: isDisabled
911
- }, props), {
1042
+ __spreadProps(__spreadValues({}, domRest), {
1043
+ className: rootClass,
1044
+ disabled: isDisabled,
912
1045
  children: icon
913
1046
  })
914
1047
  );
@@ -1758,6 +1891,10 @@ __styleInject(`@charset "UTF-8";
1758
1891
  margin: 0;
1759
1892
  }
1760
1893
 
1894
+ .Card-module_danger .Card-module_title {
1895
+ color: var(--color-error);
1896
+ }
1897
+
1761
1898
  .Card-module_description {
1762
1899
  font-size: 13px;
1763
1900
  font-weight: 400;
@@ -1787,7 +1924,7 @@ __styleInject(`@charset "UTF-8";
1787
1924
  .Card-module_spacer {
1788
1925
  display: block;
1789
1926
  }`);
1790
- var __default21 = { "root": "Card-module_root", "imageWrapper": "Card-module_imageWrapper", "image": "Card-module_image", "body": "Card-module_body", "bodyWithImage": "Card-module_bodyWithImage", "bodyNoImage": "Card-module_bodyNoImage", "titleBlock": "Card-module_titleBlock", "title": "Card-module_title", "description": "Card-module_description", "footer": "Card-module_footer", "footerRow": "Card-module_footerRow", "badges": "Card-module_badges", "spacer": "Card-module_spacer" };
1927
+ var __default21 = { "root": "Card-module_root", "imageWrapper": "Card-module_imageWrapper", "image": "Card-module_image", "body": "Card-module_body", "bodyWithImage": "Card-module_bodyWithImage", "bodyNoImage": "Card-module_bodyNoImage", "titleBlock": "Card-module_titleBlock", "title": "Card-module_title", "danger": "Card-module_danger", "description": "Card-module_description", "footer": "Card-module_footer", "footerRow": "Card-module_footerRow", "badges": "Card-module_badges", "spacer": "Card-module_spacer" };
1791
1928
 
1792
1929
  // css-inject-scss:/Users/dimakozh/Desktop/projects/kapustin.cc/packages/ui/src/molecules/IconBadge.module.scss
1793
1930
  __styleInject(`@charset "UTF-8";
@@ -1815,16 +1952,19 @@ function Card({
1815
1952
  image,
1816
1953
  title,
1817
1954
  description,
1955
+ descriptionClassName = "",
1818
1956
  badges,
1819
1957
  action,
1820
1958
  footer,
1821
1959
  children,
1960
+ variant = "default",
1822
1961
  className = ""
1823
1962
  }) {
1824
1963
  const hasImage = !!image;
1825
1964
  const hasBadgesOrAction = !!(badges && badges.length > 0 || action);
1826
1965
  const showFooter = !!footer || hasBadgesOrAction;
1827
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${__default21.root}${className ? ` ${className}` : ""}`, children: [
1966
+ const variantClass = variant === "danger" ? ` ${__default21.danger}` : "";
1967
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${__default21.root}${variantClass}${className ? ` ${className}` : ""}`, children: [
1828
1968
  hasImage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: __default21.imageWrapper, children: typeof image === "string" ? (
1829
1969
  // eslint-disable-next-line @next/next/no-img-element
1830
1970
  /* @__PURE__ */ jsxRuntime.jsx("img", { src: image, alt: title || "", className: __default21.image })
@@ -1832,7 +1972,7 @@ function Card({
1832
1972
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${__default21.body} ${hasImage ? __default21.bodyWithImage : __default21.bodyNoImage}`, children: [
1833
1973
  (title || description) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: __default21.titleBlock, children: [
1834
1974
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: __default21.title, children: title }),
1835
- description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: __default21.description, children: description })
1975
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${__default21.description}${descriptionClassName ? ` ${descriptionClassName}` : ""}`, children: description })
1836
1976
  ] }),
1837
1977
  children,
1838
1978
  showFooter && /* @__PURE__ */ jsxRuntime.jsx("div", { className: __default21.footer, children: footer || /* @__PURE__ */ jsxRuntime.jsxs("div", { className: __default21.footerRow, children: [
@@ -4030,6 +4170,18 @@ __styleInject(`@charset "UTF-8";
4030
4170
  margin-bottom: 1.5rem;
4031
4171
  }
4032
4172
 
4173
+ /* Compact layout \u2014 logo \u0438 nav \u0432\u043F\u043B\u043E\u0442\u043D\u0443\u044E \u0441\u0432\u0435\u0440\u0445\u0443, footer \u043F\u0440\u0438\u0436\u0430\u0442 \u043A \u043D\u0438\u0437\u0443 */
4174
+ .Sidebar-module_compact {
4175
+ justify-content: flex-start;
4176
+ gap: 1rem;
4177
+ }
4178
+ .Sidebar-module_compact .Sidebar-module_logo {
4179
+ margin-bottom: 0;
4180
+ }
4181
+ .Sidebar-module_compact .Sidebar-module_footer {
4182
+ margin-top: auto;
4183
+ }
4184
+
4033
4185
  .Sidebar-module_nav {
4034
4186
  display: flex;
4035
4187
  flex-direction: column;
@@ -4088,7 +4240,7 @@ __styleInject(`@charset "UTF-8";
4088
4240
  display: block;
4089
4241
  }
4090
4242
  }`);
4091
- var __default47 = { "root": "Sidebar-module_root", "menu": "Sidebar-module_menu", "courseSubmenu": "Sidebar-module_courseSubmenu", "logo": "Sidebar-module_logo", "nav": "Sidebar-module_nav", "footer": "Sidebar-module_footer", "legal": "Sidebar-module_legal", "overlay": "Sidebar-module_overlay", "collapsed": "Sidebar-module_collapsed", "open": "Sidebar-module_open", "overlayVisible": "Sidebar-module_overlayVisible" };
4243
+ var __default47 = { "root": "Sidebar-module_root", "menu": "Sidebar-module_menu", "courseSubmenu": "Sidebar-module_courseSubmenu", "logo": "Sidebar-module_logo", "compact": "Sidebar-module_compact", "footer": "Sidebar-module_footer", "nav": "Sidebar-module_nav", "legal": "Sidebar-module_legal", "overlay": "Sidebar-module_overlay", "collapsed": "Sidebar-module_collapsed", "open": "Sidebar-module_open", "overlayVisible": "Sidebar-module_overlayVisible" };
4092
4244
  var defaultMenuItems = [
4093
4245
  "\u0413\u043B\u0430\u0432\u043D\u0430\u044F",
4094
4246
  "\u041C\u043E\u0438 \u043A\u0443\u0440\u0441\u044B",
@@ -4125,7 +4277,8 @@ function Sidebar({
4125
4277
  logo,
4126
4278
  legalText,
4127
4279
  collapsed,
4128
- onToggle
4280
+ onToggle,
4281
+ layout = "space-between"
4129
4282
  }) {
4130
4283
  const rawItems = menuItems || (type === "courseSubmenu" ? courseMenuItems : defaultMenuItems);
4131
4284
  const items = normaliseItems(rawItems);
@@ -4151,7 +4304,7 @@ function Sidebar({
4151
4304
  /* @__PURE__ */ jsxRuntime.jsxs(
4152
4305
  "aside",
4153
4306
  {
4154
- className: `${__default47.root} ${widthClass}${collapseClasses ? ` ${collapseClasses}` : ""}${className ? ` ${className}` : ""}`,
4307
+ className: `${__default47.root} ${widthClass}${layout === "compact" ? ` ${__default47.compact}` : ""}${collapseClasses ? ` ${collapseClasses}` : ""}${className ? ` ${className}` : ""}`,
4155
4308
  children: [
4156
4309
  logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: __default47.logo, children: logo }),
4157
4310
  /* @__PURE__ */ jsxRuntime.jsx("nav", { className: __default47.nav, children: items.map((item, i) => {
package/dist/index.d.cts CHANGED
@@ -1,17 +1,29 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React$1, { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, ImgHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes, AnchorHTMLAttributes } from 'react';
2
+ import React$1, { ReactNode, ButtonHTMLAttributes, AnchorHTMLAttributes, InputHTMLAttributes, ImgHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes } from 'react';
3
3
 
4
- type ButtonVariant = 'primary' | 'ghost' | 'secondary' | 'outline';
4
+ type ButtonVariant = 'primary' | 'ghost' | 'secondary' | 'outline' | 'danger';
5
5
  type ButtonSize = 'sm' | 'md' | 'hero';
6
- interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
6
+ type AnchorTarget$1 = '_self' | '_blank' | '_parent' | '_top';
7
+ interface CommonProps$1 {
7
8
  variant?: ButtonVariant;
8
9
  size?: ButtonSize;
9
10
  loading?: boolean;
10
11
  icon?: ReactNode;
11
12
  iconRight?: ReactNode;
12
13
  children: ReactNode;
14
+ className?: string;
15
+ disabled?: boolean;
16
+ }
17
+ interface ButtonAsButton extends CommonProps$1, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps$1 | 'href'> {
18
+ href?: undefined;
13
19
  }
14
- declare function Button({ variant, size, loading, icon, iconRight, children, disabled, className, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
20
+ interface ButtonAsAnchor extends CommonProps$1, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps$1 | 'href' | 'target' | 'rel'> {
21
+ href: string;
22
+ target?: AnchorTarget$1;
23
+ rel?: string;
24
+ }
25
+ type ButtonProps = ButtonAsButton | ButtonAsAnchor;
26
+ declare function Button(props: ButtonProps): react_jsx_runtime.JSX.Element;
15
27
 
16
28
  interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
17
29
  label?: string;
@@ -82,12 +94,23 @@ interface MenuItemProps {
82
94
  declare function MenuItem({ text, active, onClick, className, icon }: MenuItemProps): react_jsx_runtime.JSX.Element;
83
95
 
84
96
  type IconButtonVariant = 'primary' | 'secondary' | 'clear' | 'disabled' | 'contrast';
85
- interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
97
+ type AnchorTarget = '_self' | '_blank' | '_parent' | '_top';
98
+ interface CommonProps {
86
99
  icon: ReactNode;
87
100
  variant?: IconButtonVariant;
88
101
  className?: string;
102
+ disabled?: boolean;
89
103
  }
90
- declare function IconButton({ icon, variant, className, disabled, ...props }: IconButtonProps): react_jsx_runtime.JSX.Element;
104
+ interface IconButtonAsButton extends CommonProps, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps | 'href'> {
105
+ href?: undefined;
106
+ }
107
+ interface IconButtonAsAnchor extends CommonProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps | 'href' | 'target' | 'rel'> {
108
+ href: string;
109
+ target?: AnchorTarget;
110
+ rel?: string;
111
+ }
112
+ type IconButtonProps = IconButtonAsButton | IconButtonAsAnchor;
113
+ declare function IconButton(props: IconButtonProps): react_jsx_runtime.JSX.Element;
91
114
 
92
115
  interface LogoProps {
93
116
  /** Полный override через произвольный JSX. Имеет приоритет над text/src. */
@@ -242,13 +265,17 @@ interface CardProps {
242
265
  image?: string | ReactNode;
243
266
  title?: string;
244
267
  description?: string;
268
+ /** Доп. класс для `<p class="description">` — для override (word-break, overflow-wrap, etc). */
269
+ descriptionClassName?: string;
245
270
  badges?: ReactNode[];
246
271
  action?: CardAction;
247
272
  footer?: ReactNode;
248
273
  children?: ReactNode;
274
+ /** Визуальный вариант. `'danger'` красит `.title` в `var(--color-error)`. Border остаётся default. */
275
+ variant?: 'default' | 'danger';
249
276
  className?: string;
250
277
  }
251
- declare function Card({ image, title, description, badges, action, footer, children, className, }: CardProps): react_jsx_runtime.JSX.Element;
278
+ declare function Card({ image, title, description, descriptionClassName, badges, action, footer, children, variant, className, }: CardProps): react_jsx_runtime.JSX.Element;
252
279
 
253
280
  interface IconBadgeProps {
254
281
  icon: ReactNode;
@@ -559,8 +586,14 @@ interface SidebarProps {
559
586
  collapsed?: boolean;
560
587
  /** Called when the user requests toggling the sidebar (e.g. clicking the overlay backdrop). */
561
588
  onToggle?: () => void;
589
+ /**
590
+ * Вертикальная раскладка детей.
591
+ * - `'space-between'` (default, BC) — logo / nav / footer распределены через `justify-content: space-between`.
592
+ * - `'compact'` — logo и nav сверху без зазора, footer прижимается к низу через `margin-top: auto`. Логотип теряет авто-`margin-bottom: 1.5rem`. Use-case: Select / site-switcher над nav.
593
+ */
594
+ layout?: 'space-between' | 'compact';
562
595
  }
563
- declare function Sidebar({ type, menuItems, footer, className, activeId, onItemClick, logo, legalText, collapsed, onToggle, }: SidebarProps): react_jsx_runtime.JSX.Element;
596
+ declare function Sidebar({ type, menuItems, footer, className, activeId, onItemClick, logo, legalText, collapsed, onToggle, layout, }: SidebarProps): react_jsx_runtime.JSX.Element;
564
597
 
565
598
  type AppCardVariant = 'default' | 'stamp' | 'stamp-padded';
566
599
  interface AppCardProps {
package/dist/index.d.ts CHANGED
@@ -1,17 +1,29 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React$1, { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, ImgHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes, AnchorHTMLAttributes } from 'react';
2
+ import React$1, { ReactNode, ButtonHTMLAttributes, AnchorHTMLAttributes, InputHTMLAttributes, ImgHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes } from 'react';
3
3
 
4
- type ButtonVariant = 'primary' | 'ghost' | 'secondary' | 'outline';
4
+ type ButtonVariant = 'primary' | 'ghost' | 'secondary' | 'outline' | 'danger';
5
5
  type ButtonSize = 'sm' | 'md' | 'hero';
6
- interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
6
+ type AnchorTarget$1 = '_self' | '_blank' | '_parent' | '_top';
7
+ interface CommonProps$1 {
7
8
  variant?: ButtonVariant;
8
9
  size?: ButtonSize;
9
10
  loading?: boolean;
10
11
  icon?: ReactNode;
11
12
  iconRight?: ReactNode;
12
13
  children: ReactNode;
14
+ className?: string;
15
+ disabled?: boolean;
16
+ }
17
+ interface ButtonAsButton extends CommonProps$1, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps$1 | 'href'> {
18
+ href?: undefined;
13
19
  }
14
- declare function Button({ variant, size, loading, icon, iconRight, children, disabled, className, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
20
+ interface ButtonAsAnchor extends CommonProps$1, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps$1 | 'href' | 'target' | 'rel'> {
21
+ href: string;
22
+ target?: AnchorTarget$1;
23
+ rel?: string;
24
+ }
25
+ type ButtonProps = ButtonAsButton | ButtonAsAnchor;
26
+ declare function Button(props: ButtonProps): react_jsx_runtime.JSX.Element;
15
27
 
16
28
  interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
17
29
  label?: string;
@@ -82,12 +94,23 @@ interface MenuItemProps {
82
94
  declare function MenuItem({ text, active, onClick, className, icon }: MenuItemProps): react_jsx_runtime.JSX.Element;
83
95
 
84
96
  type IconButtonVariant = 'primary' | 'secondary' | 'clear' | 'disabled' | 'contrast';
85
- interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
97
+ type AnchorTarget = '_self' | '_blank' | '_parent' | '_top';
98
+ interface CommonProps {
86
99
  icon: ReactNode;
87
100
  variant?: IconButtonVariant;
88
101
  className?: string;
102
+ disabled?: boolean;
89
103
  }
90
- declare function IconButton({ icon, variant, className, disabled, ...props }: IconButtonProps): react_jsx_runtime.JSX.Element;
104
+ interface IconButtonAsButton extends CommonProps, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps | 'href'> {
105
+ href?: undefined;
106
+ }
107
+ interface IconButtonAsAnchor extends CommonProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps | 'href' | 'target' | 'rel'> {
108
+ href: string;
109
+ target?: AnchorTarget;
110
+ rel?: string;
111
+ }
112
+ type IconButtonProps = IconButtonAsButton | IconButtonAsAnchor;
113
+ declare function IconButton(props: IconButtonProps): react_jsx_runtime.JSX.Element;
91
114
 
92
115
  interface LogoProps {
93
116
  /** Полный override через произвольный JSX. Имеет приоритет над text/src. */
@@ -242,13 +265,17 @@ interface CardProps {
242
265
  image?: string | ReactNode;
243
266
  title?: string;
244
267
  description?: string;
268
+ /** Доп. класс для `<p class="description">` — для override (word-break, overflow-wrap, etc). */
269
+ descriptionClassName?: string;
245
270
  badges?: ReactNode[];
246
271
  action?: CardAction;
247
272
  footer?: ReactNode;
248
273
  children?: ReactNode;
274
+ /** Визуальный вариант. `'danger'` красит `.title` в `var(--color-error)`. Border остаётся default. */
275
+ variant?: 'default' | 'danger';
249
276
  className?: string;
250
277
  }
251
- declare function Card({ image, title, description, badges, action, footer, children, className, }: CardProps): react_jsx_runtime.JSX.Element;
278
+ declare function Card({ image, title, description, descriptionClassName, badges, action, footer, children, variant, className, }: CardProps): react_jsx_runtime.JSX.Element;
252
279
 
253
280
  interface IconBadgeProps {
254
281
  icon: ReactNode;
@@ -559,8 +586,14 @@ interface SidebarProps {
559
586
  collapsed?: boolean;
560
587
  /** Called when the user requests toggling the sidebar (e.g. clicking the overlay backdrop). */
561
588
  onToggle?: () => void;
589
+ /**
590
+ * Вертикальная раскладка детей.
591
+ * - `'space-between'` (default, BC) — logo / nav / footer распределены через `justify-content: space-between`.
592
+ * - `'compact'` — logo и nav сверху без зазора, footer прижимается к низу через `margin-top: auto`. Логотип теряет авто-`margin-bottom: 1.5rem`. Use-case: Select / site-switcher над nav.
593
+ */
594
+ layout?: 'space-between' | 'compact';
562
595
  }
563
- declare function Sidebar({ type, menuItems, footer, className, activeId, onItemClick, logo, legalText, collapsed, onToggle, }: SidebarProps): react_jsx_runtime.JSX.Element;
596
+ declare function Sidebar({ type, menuItems, footer, className, activeId, onItemClick, logo, legalText, collapsed, onToggle, layout, }: SidebarProps): react_jsx_runtime.JSX.Element;
564
597
 
565
598
  type AppCardVariant = 'default' | 'stamp' | 'stamp-padded';
566
599
  interface AppCardProps {
package/dist/index.js CHANGED
@@ -188,6 +188,31 @@ __styleInject(`@charset "UTF-8";
188
188
  background: var(--border-color);
189
189
  }
190
190
 
191
+ .Button-module_danger {
192
+ background: var(--color-error);
193
+ border: 1px solid var(--color-error);
194
+ color: #ffffff;
195
+ box-shadow: var(--shadow-sm);
196
+ }
197
+ .Button-module_danger:hover:not(:disabled) {
198
+ background: var(--color-error-dark);
199
+ border-color: var(--color-error-dark);
200
+ box-shadow: var(--shadow-md);
201
+ }
202
+ .Button-module_danger:active:not(:disabled) {
203
+ background: var(--color-error-dark);
204
+ border-color: var(--color-error-dark);
205
+ }
206
+ .Button-module_danger:focus-visible {
207
+ outline: 2px solid var(--color-error-light);
208
+ outline-offset: 2px;
209
+ }
210
+ .Button-module_danger:disabled {
211
+ background: var(--color-error-light);
212
+ border-color: var(--color-error-light);
213
+ color: rgba(255, 255, 255, 0.7);
214
+ }
215
+
191
216
  /* \u2500\u2500\u2500 Sizes \u2500\u2500\u2500 */
192
217
  .Button-module_sm {
193
218
  font-size: 13px;
@@ -222,9 +247,9 @@ __styleInject(`@charset "UTF-8";
222
247
  cursor: not-allowed;
223
248
  pointer-events: none;
224
249
  }`);
225
- var __default2 = { "root": "Button-module_root", "primary": "Button-module_primary", "ghost": "Button-module_ghost", "secondary": "Button-module_secondary", "outline": "Button-module_outline", "sm": "Button-module_sm", "md": "Button-module_md", "hero": "Button-module_hero", "disabled": "Button-module_disabled" };
226
- function Button(_a) {
227
- var _b = _a, {
250
+ var __default2 = { "root": "Button-module_root", "primary": "Button-module_primary", "ghost": "Button-module_ghost", "secondary": "Button-module_secondary", "outline": "Button-module_outline", "danger": "Button-module_danger", "sm": "Button-module_sm", "md": "Button-module_md", "hero": "Button-module_hero", "disabled": "Button-module_disabled" };
251
+ function Button(props) {
252
+ const {
228
253
  variant = "primary",
229
254
  size = "md",
230
255
  loading = false,
@@ -233,7 +258,70 @@ function Button(_a) {
233
258
  children,
234
259
  disabled,
235
260
  className = ""
236
- } = _b, props = __objRest(_b, [
261
+ } = props;
262
+ const rootClass = `${__default2.root} ${__default2[variant]} ${__default2[size]}${disabled || loading ? ` ${__default2.disabled}` : ""}${className ? ` ${className}` : ""}`;
263
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
264
+ loading ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : icon,
265
+ children,
266
+ iconRight
267
+ ] });
268
+ if (props.href !== void 0) {
269
+ const _a = props, {
270
+ variant: _v2,
271
+ size: _s2,
272
+ loading: _l2,
273
+ icon: _i2,
274
+ iconRight: _ir2,
275
+ children: _c2,
276
+ disabled: _d2,
277
+ className: _cn2,
278
+ href: href,
279
+ target,
280
+ rel,
281
+ onClick
282
+ } = _a, domRest2 = __objRest(_a, [
283
+ // strip internal/non-DOM props
284
+ "variant",
285
+ "size",
286
+ "loading",
287
+ "icon",
288
+ "iconRight",
289
+ "children",
290
+ "disabled",
291
+ "className",
292
+ // pull out anchor-specific that we set explicitly below
293
+ "href",
294
+ "target",
295
+ "rel",
296
+ "onClick"
297
+ ]);
298
+ const safeRel = target === "_blank" ? rel != null ? rel : "noopener noreferrer" : rel;
299
+ const handleClick = disabled || loading ? (e) => e.preventDefault() : onClick;
300
+ return /* @__PURE__ */ jsx(
301
+ "a",
302
+ __spreadProps(__spreadValues({}, domRest2), {
303
+ href,
304
+ target,
305
+ rel: safeRel,
306
+ className: rootClass,
307
+ "aria-disabled": disabled || loading || void 0,
308
+ tabIndex: disabled || loading ? -1 : domRest2.tabIndex,
309
+ onClick: handleClick,
310
+ children: content
311
+ })
312
+ );
313
+ }
314
+ const _b = props, {
315
+ variant: _v,
316
+ size: _s,
317
+ loading: _l,
318
+ icon: _i,
319
+ iconRight: _ir,
320
+ children: _c,
321
+ disabled: _d,
322
+ className: _cn,
323
+ href: _href
324
+ } = _b, domRest = __objRest(_b, [
237
325
  "variant",
238
326
  "size",
239
327
  "loading",
@@ -241,19 +329,15 @@ function Button(_a) {
241
329
  "iconRight",
242
330
  "children",
243
331
  "disabled",
244
- "className"
332
+ "className",
333
+ "href"
245
334
  ]);
246
- return /* @__PURE__ */ jsxs(
335
+ return /* @__PURE__ */ jsx(
247
336
  "button",
248
- __spreadProps(__spreadValues({
249
- className: `${__default2.root} ${__default2[variant]} ${__default2[size]}${disabled || loading ? ` ${__default2.disabled}` : ""}${className ? ` ${className}` : ""}`,
250
- disabled: disabled || loading
251
- }, props), {
252
- children: [
253
- loading ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : icon,
254
- children,
255
- iconRight
256
- ]
337
+ __spreadProps(__spreadValues({}, domRest), {
338
+ className: rootClass,
339
+ disabled: disabled || loading,
340
+ children: content
257
341
  })
258
342
  );
259
343
  }
@@ -894,15 +978,64 @@ __styleInject(`@charset "UTF-8";
894
978
  cursor: not-allowed;
895
979
  }`);
896
980
  var __default10 = { "root": "IconButton-module_root", "primary": "IconButton-module_primary", "secondary": "IconButton-module_secondary", "clear": "IconButton-module_clear", "disabled": "IconButton-module_disabled", "contrast": "IconButton-module_contrast", "isDisabled": "IconButton-module_isDisabled" };
897
- function IconButton(_a) {
898
- var _b = _a, { icon, variant = "primary", className = "", disabled } = _b, props = __objRest(_b, ["icon", "variant", "className", "disabled"]);
981
+ function IconButton(props) {
982
+ const { icon, variant = "primary", className = "", disabled } = props;
899
983
  const isDisabled = variant === "disabled" || disabled;
984
+ const rootClass = `${__default10.root} ${__default10[variant]}${isDisabled ? ` ${__default10.isDisabled}` : ""}${className ? ` ${className}` : ""}`;
985
+ if (props.href !== void 0) {
986
+ const _a = props, {
987
+ icon: _i2,
988
+ variant: _v2,
989
+ className: _cn2,
990
+ disabled: _d2,
991
+ href,
992
+ target,
993
+ rel,
994
+ onClick
995
+ } = _a, domRest2 = __objRest(_a, [
996
+ "icon",
997
+ "variant",
998
+ "className",
999
+ "disabled",
1000
+ "href",
1001
+ "target",
1002
+ "rel",
1003
+ "onClick"
1004
+ ]);
1005
+ const safeRel = target === "_blank" ? rel != null ? rel : "noopener noreferrer" : rel;
1006
+ const handleClick = isDisabled ? (e) => e.preventDefault() : onClick;
1007
+ return /* @__PURE__ */ jsx(
1008
+ "a",
1009
+ __spreadProps(__spreadValues({}, domRest2), {
1010
+ href,
1011
+ target,
1012
+ rel: safeRel,
1013
+ className: rootClass,
1014
+ "aria-disabled": isDisabled || void 0,
1015
+ tabIndex: isDisabled ? -1 : domRest2.tabIndex,
1016
+ onClick: handleClick,
1017
+ children: icon
1018
+ })
1019
+ );
1020
+ }
1021
+ const _b = props, {
1022
+ icon: _i,
1023
+ variant: _v,
1024
+ className: _cn,
1025
+ disabled: _d,
1026
+ href: _href
1027
+ } = _b, domRest = __objRest(_b, [
1028
+ "icon",
1029
+ "variant",
1030
+ "className",
1031
+ "disabled",
1032
+ "href"
1033
+ ]);
900
1034
  return /* @__PURE__ */ jsx(
901
1035
  "button",
902
- __spreadProps(__spreadValues({
903
- className: `${__default10.root} ${__default10[variant]}${isDisabled ? ` ${__default10.isDisabled}` : ""}${className ? ` ${className}` : ""}`,
904
- disabled: isDisabled
905
- }, props), {
1036
+ __spreadProps(__spreadValues({}, domRest), {
1037
+ className: rootClass,
1038
+ disabled: isDisabled,
906
1039
  children: icon
907
1040
  })
908
1041
  );
@@ -1752,6 +1885,10 @@ __styleInject(`@charset "UTF-8";
1752
1885
  margin: 0;
1753
1886
  }
1754
1887
 
1888
+ .Card-module_danger .Card-module_title {
1889
+ color: var(--color-error);
1890
+ }
1891
+
1755
1892
  .Card-module_description {
1756
1893
  font-size: 13px;
1757
1894
  font-weight: 400;
@@ -1781,7 +1918,7 @@ __styleInject(`@charset "UTF-8";
1781
1918
  .Card-module_spacer {
1782
1919
  display: block;
1783
1920
  }`);
1784
- var __default21 = { "root": "Card-module_root", "imageWrapper": "Card-module_imageWrapper", "image": "Card-module_image", "body": "Card-module_body", "bodyWithImage": "Card-module_bodyWithImage", "bodyNoImage": "Card-module_bodyNoImage", "titleBlock": "Card-module_titleBlock", "title": "Card-module_title", "description": "Card-module_description", "footer": "Card-module_footer", "footerRow": "Card-module_footerRow", "badges": "Card-module_badges", "spacer": "Card-module_spacer" };
1921
+ var __default21 = { "root": "Card-module_root", "imageWrapper": "Card-module_imageWrapper", "image": "Card-module_image", "body": "Card-module_body", "bodyWithImage": "Card-module_bodyWithImage", "bodyNoImage": "Card-module_bodyNoImage", "titleBlock": "Card-module_titleBlock", "title": "Card-module_title", "danger": "Card-module_danger", "description": "Card-module_description", "footer": "Card-module_footer", "footerRow": "Card-module_footerRow", "badges": "Card-module_badges", "spacer": "Card-module_spacer" };
1785
1922
 
1786
1923
  // css-inject-scss:/Users/dimakozh/Desktop/projects/kapustin.cc/packages/ui/src/molecules/IconBadge.module.scss
1787
1924
  __styleInject(`@charset "UTF-8";
@@ -1809,16 +1946,19 @@ function Card({
1809
1946
  image,
1810
1947
  title,
1811
1948
  description,
1949
+ descriptionClassName = "",
1812
1950
  badges,
1813
1951
  action,
1814
1952
  footer,
1815
1953
  children,
1954
+ variant = "default",
1816
1955
  className = ""
1817
1956
  }) {
1818
1957
  const hasImage = !!image;
1819
1958
  const hasBadgesOrAction = !!(badges && badges.length > 0 || action);
1820
1959
  const showFooter = !!footer || hasBadgesOrAction;
1821
- return /* @__PURE__ */ jsxs("div", { className: `${__default21.root}${className ? ` ${className}` : ""}`, children: [
1960
+ const variantClass = variant === "danger" ? ` ${__default21.danger}` : "";
1961
+ return /* @__PURE__ */ jsxs("div", { className: `${__default21.root}${variantClass}${className ? ` ${className}` : ""}`, children: [
1822
1962
  hasImage && /* @__PURE__ */ jsx("div", { className: __default21.imageWrapper, children: typeof image === "string" ? (
1823
1963
  // eslint-disable-next-line @next/next/no-img-element
1824
1964
  /* @__PURE__ */ jsx("img", { src: image, alt: title || "", className: __default21.image })
@@ -1826,7 +1966,7 @@ function Card({
1826
1966
  /* @__PURE__ */ jsxs("div", { className: `${__default21.body} ${hasImage ? __default21.bodyWithImage : __default21.bodyNoImage}`, children: [
1827
1967
  (title || description) && /* @__PURE__ */ jsxs("div", { className: __default21.titleBlock, children: [
1828
1968
  title && /* @__PURE__ */ jsx("h3", { className: __default21.title, children: title }),
1829
- description && /* @__PURE__ */ jsx("p", { className: __default21.description, children: description })
1969
+ description && /* @__PURE__ */ jsx("p", { className: `${__default21.description}${descriptionClassName ? ` ${descriptionClassName}` : ""}`, children: description })
1830
1970
  ] }),
1831
1971
  children,
1832
1972
  showFooter && /* @__PURE__ */ jsx("div", { className: __default21.footer, children: footer || /* @__PURE__ */ jsxs("div", { className: __default21.footerRow, children: [
@@ -4024,6 +4164,18 @@ __styleInject(`@charset "UTF-8";
4024
4164
  margin-bottom: 1.5rem;
4025
4165
  }
4026
4166
 
4167
+ /* Compact layout \u2014 logo \u0438 nav \u0432\u043F\u043B\u043E\u0442\u043D\u0443\u044E \u0441\u0432\u0435\u0440\u0445\u0443, footer \u043F\u0440\u0438\u0436\u0430\u0442 \u043A \u043D\u0438\u0437\u0443 */
4168
+ .Sidebar-module_compact {
4169
+ justify-content: flex-start;
4170
+ gap: 1rem;
4171
+ }
4172
+ .Sidebar-module_compact .Sidebar-module_logo {
4173
+ margin-bottom: 0;
4174
+ }
4175
+ .Sidebar-module_compact .Sidebar-module_footer {
4176
+ margin-top: auto;
4177
+ }
4178
+
4027
4179
  .Sidebar-module_nav {
4028
4180
  display: flex;
4029
4181
  flex-direction: column;
@@ -4082,7 +4234,7 @@ __styleInject(`@charset "UTF-8";
4082
4234
  display: block;
4083
4235
  }
4084
4236
  }`);
4085
- var __default47 = { "root": "Sidebar-module_root", "menu": "Sidebar-module_menu", "courseSubmenu": "Sidebar-module_courseSubmenu", "logo": "Sidebar-module_logo", "nav": "Sidebar-module_nav", "footer": "Sidebar-module_footer", "legal": "Sidebar-module_legal", "overlay": "Sidebar-module_overlay", "collapsed": "Sidebar-module_collapsed", "open": "Sidebar-module_open", "overlayVisible": "Sidebar-module_overlayVisible" };
4237
+ var __default47 = { "root": "Sidebar-module_root", "menu": "Sidebar-module_menu", "courseSubmenu": "Sidebar-module_courseSubmenu", "logo": "Sidebar-module_logo", "compact": "Sidebar-module_compact", "footer": "Sidebar-module_footer", "nav": "Sidebar-module_nav", "legal": "Sidebar-module_legal", "overlay": "Sidebar-module_overlay", "collapsed": "Sidebar-module_collapsed", "open": "Sidebar-module_open", "overlayVisible": "Sidebar-module_overlayVisible" };
4086
4238
  var defaultMenuItems = [
4087
4239
  "\u0413\u043B\u0430\u0432\u043D\u0430\u044F",
4088
4240
  "\u041C\u043E\u0438 \u043A\u0443\u0440\u0441\u044B",
@@ -4119,7 +4271,8 @@ function Sidebar({
4119
4271
  logo,
4120
4272
  legalText,
4121
4273
  collapsed,
4122
- onToggle
4274
+ onToggle,
4275
+ layout = "space-between"
4123
4276
  }) {
4124
4277
  const rawItems = menuItems || (type === "courseSubmenu" ? courseMenuItems : defaultMenuItems);
4125
4278
  const items = normaliseItems(rawItems);
@@ -4145,7 +4298,7 @@ function Sidebar({
4145
4298
  /* @__PURE__ */ jsxs(
4146
4299
  "aside",
4147
4300
  {
4148
- className: `${__default47.root} ${widthClass}${collapseClasses ? ` ${collapseClasses}` : ""}${className ? ` ${className}` : ""}`,
4301
+ className: `${__default47.root} ${widthClass}${layout === "compact" ? ` ${__default47.compact}` : ""}${collapseClasses ? ` ${collapseClasses}` : ""}${className ? ` ${className}` : ""}`,
4149
4302
  children: [
4150
4303
  logo && /* @__PURE__ */ jsx("div", { className: __default47.logo, children: logo }),
4151
4304
  /* @__PURE__ */ jsx("nav", { className: __default47.nav, children: items.map((item, i) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmitriikapustin/ui",
3
- "version": "0.2.13",
3
+ "version": "0.3.2",
4
4
  "description": "Universal UI/UX Kit — React 19 component library with Atomic Design, CSS custom properties, and SCSS modules",
5
5
  "author": "Kapustin Team",
6
6
  "license": "MIT",