@andreyfedkovich/cozy-ui 0.5.0 → 0.6.1

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 CHANGED
@@ -1,6 +1,45 @@
1
1
  # Changelog
2
2
 
3
- ## 0.2.1
3
+ Формат основан на [Keep a Changelog](https://keepachangelog.com/).
4
+ Версии соответствуют [Semantic Versioning](https://semver.org/) и git-тегам `v*`.
5
+
6
+ ## 0.6.0 - 2026-05-21
7
+
8
+ - **feat:** `SideNav` — vertical navigation with `classic` and `aurora` variants, optional collapse, declarative `sections`, composition API (`SideNav.Section`, `SideNav.Item`, `SideNav.Divider`, `SideNav.Custom`), user block, and nested submenu items.
9
+ - **feat:** `Textarea` — multiline field with `label`, `error`, `hint` / `hintVariant`, optional label tooltip (`tooltipContent`), and `forwardRef`.
10
+
11
+ ## 0.5.2 - 2026-05-20
12
+
13
+ - **fix:** Published `styles.css` now includes Tailwind utilities and design tokens used by `Calendar` and other `src/components/ui` primitives. Consumers only need `import "@andreyfedkovich/cozy-ui/styles.css"` — Tailwind in the host app is not required.
14
+ - **build:** `build:lib` runs `@tailwindcss/cli` on `src/lib/tailwind.css` and merges output with SCSS module CSS.
15
+
16
+ ## 0.5.1 - 2026-05-20
17
+
18
+ - Republish of 0.5.0 (no code changes).
19
+
20
+ ## 0.5.0 - 2026-05-20
21
+
22
+ - **feat:** `Calendar` date picker for forms (`yyyy-MM-dd` value, localized display).
23
+ - **feat:** `Label` and field labels for form components.
24
+ - **feat:** `Checkbox` with label, error state, and tooltip on the label.
25
+
26
+ ## 0.4.0 - 2026-05-19
27
+
28
+ - **feat:** `CommentFeed` — workflow comment thread with composer and list layout.
29
+
30
+ ## 0.3.0 - 2026-05-18
31
+
32
+ - **feat:** `DetailView` — composition-first detail page layout (`DetailView.Header`, `.Section`, `.Field`, etc.).
33
+
34
+ ## 0.2.3 - 2026-05-14
35
+
36
+ - **feat:** `TreeDialogSelect` — optional confirm-only-leaf mode for tree selection.
37
+
38
+ ## 0.2.2 - 2026-05-07
39
+
40
+ - **feat:** `TabsRounded` — rounded tab panels variant.
41
+
42
+ ## 0.2.1 - 2026-05-06
4
43
 
5
44
  - **fix:** Published `dist-lib/index.d.ts` is now the real rolled-up declaration bundle (no broken stub pointing at `../dist/lib/index`). `vite-plugin-dts` writes types to the same `outDir` as the library build (`dist-lib`).
6
45
  - **fix:** Ship `dist-lib/svg-react-shim.d.ts` and a `/// <reference />` in `index.d.ts` so consumers typecheck `*.svg?react` imports from the bundle without missing-module errors (including with `skipLibCheck: false`).
package/README.md CHANGED
@@ -31,11 +31,11 @@ npm i @andreyfedkovich/cozy-ui
31
31
  - [Design tokens](#design-tokens)
32
32
  - [Component API](#component-api)
33
33
  - [Layout & content](#layout--content) — `BaseBlock`, `Card`, `CollapsableBlock`, `Collapse`, `Carousel`, `EmptyComponent`, `Spinner`
34
- - [Inputs & forms](#inputs--forms) — `Button`, `RadioGroupButton`, `Input`, `Calendar`, `Checkbox`, `Select`, `DialogSelect`, `TreeDialogSelect`, `InputCaption`, `Label`
34
+ - [Inputs & forms](#inputs--forms) — `Button`, `RadioGroupButton`, `Input`, `Textarea`, `Calendar`, `Checkbox`, `Select`, `DialogSelect`, `TreeDialogSelect`, `InputCaption`, `Label`
35
35
  - [Navigation](#navigation) — `Tabs`, `TabsRounded`, `Stepper`
36
36
  - [Overlays](#overlays) — `Popover`, `TooltipDark`, `TooltipLight`
37
37
  - [Utility](#utility) — `Tag`, `CopyTextTrigger`
38
- - [Workflow](#workflow) — `ApprovalRoute`
38
+ - [Workflow](#workflow) — `ApprovalRoute`, `CommentFeed`, `DetailView`, `SideNav`
39
39
  - [Hooks & helpers](#hooks--helpers)
40
40
  - [Icons](#icons)
41
41
  - [TypeScript](#typescript)
@@ -86,6 +86,19 @@ import "@andreyfedkovich/cozy-ui/styles.css";
86
86
 
87
87
  Sizing uses CSS `rem` against the browser’s default root font size (commonly **16px**). You **do not** need to set `html { font-size: … }` for components to match the library demo.
88
88
 
89
+ ### Tailwind-powered components
90
+
91
+ Most Cozy UI styles come from SCSS modules bundled into `styles.css`. Components built on shadcn use Tailwind class names in JS; those utilities are **prebuilt into the same** `@andreyfedkovich/cozy-ui/styles.css` when the package is published. You do **not** need Tailwind CSS in your application.
92
+
93
+ If your app already uses Tailwind v4 and you prefer to generate utilities yourself, you may add `@source` pointing at the library bundle — optional, not required.
94
+
95
+ ### Adding a new Tailwind-based component (library authors)
96
+
97
+ 1. Primitive in `src/components/ui/<name>.tsx` (Tailwind + Radix as needed).
98
+ 2. Public API in `src/lib/components/<Name>/` (field label, errors, value; SCSS optional for the trigger shell).
99
+ 3. Ensure paths are covered by `@source` in `src/lib/tailwind.css` (`../lib/**/*`, `../components/ui/**/*`).
100
+ 4. Run `npm run build:lib` before publish; verify `dist-lib/styles.css` includes the new classes.
101
+
89
102
  ---
90
103
 
91
104
  ## Quick start
@@ -347,10 +360,47 @@ const [email, setEmail] = useState("");
347
360
  />;
348
361
  ```
349
362
 
363
+ #### `Textarea`
364
+
365
+ Accessible multiline text field with optional label and validation message for product forms.
366
+
367
+ | Prop | Type | Default | Description |
368
+ | ------------------------ | ------------------------------------------ | ------- | ---------------------------------------- |
369
+ | `label` | `ReactNode` | — | Field label above the textarea. |
370
+ | `tooltipContent` | `ReactNode` | — | Help tooltip on the «?» icon next to the label. |
371
+ | `tooltipPopperClassName` | `string` | — | Extra class for the tooltip popper. |
372
+ | `error` | `string \| null` | — | Validation message under the textarea. |
373
+ | `disabled` | `boolean` | `false` | Disabled state. |
374
+ | `className` | `string` | — | Wrapper class. |
375
+ | `textareaClassName` | `string` | — | Native `<textarea>` class. |
376
+ | `...rest` | `TextareaHTMLAttributes<HTMLTextAreaElement>` | — | All native textarea props (`rows`, `placeholder`, `value`, `onChange`, etc.). |
377
+
378
+ ```tsx
379
+ import { Textarea } from "@andreyfedkovich/cozy-ui";
380
+ import { useState } from "react";
381
+
382
+ const [comment, setComment] = useState("");
383
+
384
+ <Textarea
385
+ label="Comment"
386
+ placeholder="Write your message…"
387
+ rows={4}
388
+ value={comment}
389
+ onChange={(e) => setComment(e.target.value)}
390
+ />;
391
+
392
+ <Textarea
393
+ label="Description"
394
+ error="Description is required."
395
+ />;
396
+ ```
397
+
350
398
  #### `Calendar`
351
399
 
352
400
  Date picker field for forms. Value is stored as `yyyy-MM-dd` (or `null`); the trigger shows `dd.MM.yyyy`. Includes helpers for parsing and serializing local calendar days.
353
401
 
402
+ **Styling:** import `@andreyfedkovich/cozy-ui/styles.css` once (same as other components). Calendar popover and day grid use Tailwind utilities that are included in that file — no extra CSS or Tailwind config in your project.
403
+
354
404
  | Prop | Type | Default | Description |
355
405
  | ------------------------- | --------------------------------- | ------- | -------------------------------------------------------- |
356
406
  | `label` | `string` | — | Field label. |
@@ -754,6 +804,66 @@ Edit mode:
754
804
 
755
805
  ---
756
806
 
807
+ #### `SideNav`
808
+
809
+ Premium vertical navigation panel with a user block on top, configurable sections, optional collapse and two visual variants — `classic` (light, reference-style) and `aurora` (deep gradient with glass and glow accents). Composition-first like `DetailView`.
810
+
811
+ | Prop | Type | Default | Description |
812
+ | ------------------- | ------------------------------------- | ----------- | -------------------------------------------------------------------- |
813
+ | `user` | `SideNavUser` | — | Built-in user block (avatar + name + role + optional status badge). |
814
+ | `userSlot` | `ReactNode` | — | Fully custom header content (overrides `user`). |
815
+ | `sections` | `SideNavSection[]` | — | Declarative sections, each with `title` and `items`. |
816
+ | `children` | `ReactNode` | — | Composition: `SideNav.Section`, `SideNav.Item`, `SideNav.Divider`, `SideNav.Custom`. |
817
+ | `variant` | `"classic" \| "aurora"` | `"classic"` | Visual style switch. |
818
+ | `activeId` / `defaultActiveId` | `string` | — | Controlled / uncontrolled active item. |
819
+ | `onActiveChange` | `(id: string) => void` | — | Fires when an item is selected. |
820
+ | `collapsible` | `boolean` | `false` | Shows a collapse toggle in the header. |
821
+ | `collapsed` / `defaultCollapsed` | `boolean` | `false` | Controlled / uncontrolled collapsed state. |
822
+ | `width` / `collapsedWidth` | `number \| string` | `280` / `76` | Panel width in expanded / collapsed mode. |
823
+ | `footer` | `ReactNode` | — | Bottom slot (e.g. "Sign out"). |
824
+
825
+ Each `SideNavItem` supports `icon`, `href`, `badge`, `active`, `disabled`, `onClick` and nested `children` (renders as a collapsible submenu).
826
+
827
+ Declarative usage:
828
+
829
+ ```tsx
830
+ import { SideNav, HomeIcon, ProfileIcon, ClockIcon } from "@andreyfedkovich/cozy-ui";
831
+
832
+ <SideNav
833
+ variant="aurora"
834
+ collapsible
835
+ user={{ name: "Kate Petrova", role: "Head of Operations", badge: true }}
836
+ sections={[
837
+ { items: [{ id: "home", label: "Home", icon: <HomeIcon /> }] },
838
+ {
839
+ title: "For me",
840
+ items: [
841
+ { id: "profile", label: "My profile", icon: <ProfileIcon /> },
842
+ { id: "time", label: "Working time", icon: <ClockIcon />, badge: "3" },
843
+ ],
844
+ },
845
+ ]}
846
+ onActiveChange={(id) => console.log(id)}
847
+ />;
848
+ ```
849
+
850
+ Composition-first (like `DetailView`):
851
+
852
+ ```tsx
853
+ <SideNav user={...} variant="classic">
854
+ <SideNav.Section title="For me">
855
+ <SideNav.Item id="profile" icon={<ProfileIcon />} label="My profile" />
856
+ <SideNav.Item id="time" icon={<ClockIcon />} label="Working time" badge="3" />
857
+ </SideNav.Section>
858
+ <SideNav.Divider />
859
+ <SideNav.Section title="Custom">
860
+ <SideNav.Custom>{/* any JSX */}</SideNav.Custom>
861
+ </SideNav.Section>
862
+ </SideNav>
863
+ ```
864
+
865
+ ---
866
+
757
867
  ## Hooks & helpers
758
868
 
759
869
  ### `useMeasureElement`
@@ -849,7 +959,7 @@ For per-component overrides, every component accepts a `className` prop and uses
849
959
  ```bash
850
960
  bun install
851
961
  bun run dev # demo playground at http://localhost:5173
852
- bun run build:lib # produce dist/ (ESM + CJS + .d.ts + styles.css)
962
+ bun run build:lib # dist-lib/ (ESM + CJS + .d.ts + styles.css with SCSS + Tailwind)
853
963
  bun run lint
854
964
  bun run format
855
965
  ```
@@ -875,9 +985,10 @@ PRs are welcome. Please:
875
985
  1. Run `bun run lint && bun run format` before pushing.
876
986
  2. Add the new component to `src/lib/components/index.ts` and demo it in `src/routes/index.tsx`.
877
987
  3. Document any new prop in this README.
988
+ 4. Record library changes in `CHANGELOG.md` under **Unreleased**; when publishing to npm, move them into a dated `## X.Y.Z` section and clear **Unreleased**.
878
989
 
879
990
  ---
880
991
 
881
992
  ## License
882
993
 
883
- MIT © Andrey Fedkovich
994
+ MIT © Andrey Fedkovich
@@ -42,6 +42,7 @@ import { default as MarketIcon } from './market.svg?react';
42
42
  import { default as MegaphoneIcon } from './megaphone.svg?react';
43
43
  import { MemoExoticComponent } from 'react';
44
44
  import { default as MessageIcon } from './message.svg?react';
45
+ import { MouseEvent as MouseEvent_2 } from 'react';
45
46
  import { default as NotebookIcon } from './notebook.svg?react';
46
47
  import { default as PhoneIcon } from './phone.svg?react';
47
48
  import { default as PlaneIcon } from './plane.svg?react';
@@ -57,6 +58,7 @@ import { default as SchoolIcon } from './school.svg?react';
57
58
  import { default as SearchIcon } from './search.svg?react';
58
59
  import { default as SettingsIcon } from './settings.svg?react';
59
60
  import { default as TaskListIcon } from './taskList.svg?react';
61
+ import { TextareaHTMLAttributes } from 'react';
60
62
  import { default as TimesheetIcon } from './timesheet.svg?react';
61
63
  import { default as TrendUpIcon } from './trendUp.svg?react';
62
64
  import { default as UserSwitchIcon } from './userSwitch.svg?react';
@@ -204,6 +206,9 @@ export declare interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInpu
204
206
  label?: ReactNode;
205
207
  error?: string | null;
206
208
  checkboxClassName?: string;
209
+ /** Подсказка по наведению на иконку «?» справа от подписи */
210
+ tooltipContent?: ReactNode;
211
+ tooltipPopperClassName?: string;
207
212
  }
208
213
 
209
214
  export { CheckGreen }
@@ -368,6 +373,11 @@ export declare interface CopyTextTriggerProps {
368
373
 
369
374
  export { CrossIcon }
370
375
 
376
+ declare const CustomEl: FC<{
377
+ children?: ReactNode;
378
+ className?: string;
379
+ }>;
380
+
371
381
  export declare interface CustomOption<T, S = string> {
372
382
  value: S;
373
383
  label: string;
@@ -510,6 +520,10 @@ declare const DividerEl: FC<{
510
520
  className?: string;
511
521
  }>;
512
522
 
523
+ declare const DividerEl_2: FC<{
524
+ className?: string;
525
+ }>;
526
+
513
527
  export { DoneIcon }
514
528
 
515
529
  export { DownloadIcon }
@@ -591,6 +605,10 @@ export declare interface InputProps extends InputHTMLAttributes<HTMLInputElement
591
605
 
592
606
  export { IslandIcon }
593
607
 
608
+ declare interface ItemProps extends SideNavItem {
609
+ level?: number;
610
+ }
611
+
594
612
  export declare const Label: ({ htmlFor, children, className }: LabelProps) => JSX.Element;
595
613
 
596
614
  declare interface LabelProps {
@@ -744,6 +762,9 @@ declare interface SectionComponentProps extends Omit<DetailSection, "fields"> {
744
762
  children?: ReactNode;
745
763
  }
746
764
 
765
+ declare interface SectionProps extends SideNavSection {
766
+ }
767
+
747
768
  export declare const Select: <T, S extends string | number>({ options, value, mode, placeholder, onChange, dropdownRender, optionRender, selectedOptionRender, dropdownIcon, tagRender, dropDownClassName, optionClassName, inputClassName, deleteIconClassName, onDelete, onClear, label, tooltipContent, tooltipPopperClassName, onSearch, searchClassName, searchPlaceholder, isLoading, disabled, onClose, portalTarget, error, fixedHeight, template, columns, total, }: CustomSelectProps<T, S>) => JSX.Element;
748
769
 
749
770
  export declare type SelectColumn<T, S> = {
@@ -755,6 +776,69 @@ export declare type SelectColumn<T, S> = {
755
776
 
756
777
  export { SettingsIcon }
757
778
 
779
+ export declare const SideNav: SideNavComponent;
780
+
781
+ declare type SideNavComponent = FC<SideNavProps> & {
782
+ Section: typeof SideNavSectionEl;
783
+ Item: typeof SideNavItemEl;
784
+ Divider: typeof DividerEl_2;
785
+ Custom: typeof CustomEl;
786
+ };
787
+
788
+ export declare interface SideNavItem {
789
+ id: string;
790
+ label: ReactNode;
791
+ icon?: ReactNode;
792
+ href?: string;
793
+ active?: boolean;
794
+ badge?: ReactNode;
795
+ disabled?: boolean;
796
+ onClick?: (e: MouseEvent_2<HTMLAnchorElement | HTMLButtonElement>) => void;
797
+ children?: SideNavItem[];
798
+ }
799
+
800
+ declare const SideNavItemEl: FC<ItemProps>;
801
+
802
+ export declare interface SideNavProps {
803
+ user?: SideNavUser;
804
+ userSlot?: ReactNode;
805
+ sections?: SideNavSection[];
806
+ children?: ReactNode;
807
+ variant?: SideNavVariant;
808
+ activeId?: string;
809
+ defaultActiveId?: string;
810
+ onActiveChange?: (id: string) => void;
811
+ collapsible?: boolean;
812
+ collapsed?: boolean;
813
+ defaultCollapsed?: boolean;
814
+ onCollapsedChange?: (v: boolean) => void;
815
+ footer?: ReactNode;
816
+ width?: number | string;
817
+ collapsedWidth?: number | string;
818
+ className?: string;
819
+ }
820
+
821
+ export declare interface SideNavSection {
822
+ id?: string;
823
+ title?: ReactNode;
824
+ items?: SideNavItem[];
825
+ children?: ReactNode;
826
+ className?: string;
827
+ }
828
+
829
+ declare const SideNavSectionEl: FC<SectionProps>;
830
+
831
+ export declare interface SideNavUser {
832
+ name: string;
833
+ role?: ReactNode;
834
+ avatarUrl?: string;
835
+ initials?: string;
836
+ badge?: ReactNode;
837
+ onClick?: () => void;
838
+ }
839
+
840
+ export declare type SideNavVariant = "classic" | "aurora";
841
+
758
842
  export declare const Spinner: React_2.FC<Props>;
759
843
 
760
844
  declare type SpinnerSize = "big" | "small" | "extraSmall" | "medium" | "large";
@@ -829,6 +913,20 @@ export declare const Tag: FC<PropsWithChildren<Props_4>>;
829
913
 
830
914
  export { TaskListIcon }
831
915
 
916
+ export declare const Textarea: ForwardRefExoticComponent<TextareaProps & RefAttributes<HTMLTextAreaElement>>;
917
+
918
+ export declare interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
919
+ label?: ReactNode;
920
+ error?: string | null;
921
+ /** Neutral helper text under the textarea (hidden when `error` is set). */
922
+ hint?: ReactNode;
923
+ hintVariant?: InputCaptionVariant;
924
+ textareaClassName?: string;
925
+ /** Подсказка по наведению на иконку «?» справа от подписи */
926
+ tooltipContent?: ReactNode;
927
+ tooltipPopperClassName?: string;
928
+ }
929
+
832
930
  export { TimesheetIcon }
833
931
 
834
932
  export declare const todayLocalDay: () => Date;