@weavix/tracker-components 0.1.0 → 0.1.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 (48) hide show
  1. package/README.md +191 -19
  2. package/build/cjs/components/Icon/EntityIcon/EntityIcon.js +2 -3
  3. package/build/cjs/components/Icon/EntityIcon/EntityIcon.js.map +1 -1
  4. package/build/cjs/components/Icon/GoalIcon/README.md +123 -0
  5. package/build/cjs/components/Icon/IconRefuge/IconRefuge.classname.js.map +1 -1
  6. package/build/cjs/components/Icon/IconRefuge/IconRefuge.css +0 -3
  7. package/build/cjs/components/Icon/IconRefuge/IconRefuge.css.map +1 -1
  8. package/build/cjs/components/Icon/IssueTypeIcon/IssueTypeIcon.js +30 -50
  9. package/build/cjs/components/Icon/IssueTypeIcon/IssueTypeIcon.js.map +1 -1
  10. package/build/cjs/components/Icon/IssueTypeIcon/README.md +146 -22
  11. package/build/cjs/components/Icon/KeyResultIcon/README.md +73 -0
  12. package/build/cjs/components/Icon/MetaEntityIcon/README.md +136 -0
  13. package/build/cjs/components/Icon/PortfolioIcon/PortfolioIcon.d.ts +4 -1
  14. package/build/cjs/components/Icon/PortfolioIcon/PortfolioIcon.js +1 -0
  15. package/build/cjs/components/Icon/PortfolioIcon/PortfolioIcon.js.map +1 -1
  16. package/build/cjs/components/Icon/PortfolioIcon/README.md +94 -9
  17. package/build/cjs/components/Icon/ProjectIcon/README.md +133 -9
  18. package/build/cjs/components/MetaEntitiesSelect/README.md +10 -0
  19. package/build/cjs/components/PluginFieldsSelect/README.md +18 -0
  20. package/build/cjs/components/QueueSelect/README.md +10 -0
  21. package/build/cjs/components/TrackerDisclosure/README.md +205 -0
  22. package/build/esm/components/Icon/EntityIcon/EntityIcon.js +2 -3
  23. package/build/esm/components/Icon/EntityIcon/EntityIcon.js.map +1 -1
  24. package/build/esm/components/Icon/GoalIcon/README.md +123 -0
  25. package/build/esm/components/Icon/IconRefuge/IconRefuge.classname.js.map +1 -1
  26. package/build/esm/components/Icon/IconRefuge/IconRefuge.css +0 -3
  27. package/build/esm/components/Icon/IconRefuge/IconRefuge.css.map +1 -1
  28. package/build/esm/components/Icon/IconRefuge/IconRefuge.scss +0 -4
  29. package/build/esm/components/Icon/IssueTypeIcon/IssueTypeIcon.js +30 -50
  30. package/build/esm/components/Icon/IssueTypeIcon/IssueTypeIcon.js.map +1 -1
  31. package/build/esm/components/Icon/IssueTypeIcon/README.md +146 -22
  32. package/build/esm/components/Icon/KeyResultIcon/README.md +73 -0
  33. package/build/esm/components/Icon/MetaEntityIcon/README.md +136 -0
  34. package/build/esm/components/Icon/PortfolioIcon/PortfolioIcon.d.ts +4 -1
  35. package/build/esm/components/Icon/PortfolioIcon/PortfolioIcon.js +1 -0
  36. package/build/esm/components/Icon/PortfolioIcon/PortfolioIcon.js.map +1 -1
  37. package/build/esm/components/Icon/PortfolioIcon/README.md +94 -9
  38. package/build/esm/components/Icon/ProjectIcon/README.md +133 -9
  39. package/build/esm/components/MetaEntitiesSelect/README.md +10 -0
  40. package/build/esm/components/PluginFieldsSelect/README.md +18 -0
  41. package/build/esm/components/QueueSelect/README.md +10 -0
  42. package/build/esm/components/TrackerDisclosure/README.md +205 -0
  43. package/package.json +1 -1
  44. package/build/cjs/components/Icon/Icon.css +0 -33
  45. package/build/cjs/components/Icon/Icon.css.map +0 -1
  46. package/build/esm/components/Icon/Icon.css +0 -33
  47. package/build/esm/components/Icon/Icon.css.map +0 -1
  48. package/build/esm/components/Icon/Icon.scss +0 -46
@@ -1,51 +1,175 @@
1
1
  # ProjectIcon
2
2
 
3
3
  <!-- description:start -->
4
- Component displaying an icon for a project entity
4
+ Displays a colored icon for a Tracker project entity. The icon color is deterministically derived from the `projectId`, so the same project always renders the same color across sessions and environments.
5
5
  <!-- description:end -->
6
6
 
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import {ProjectIcon} from '@weavix/tracker-components';
11
+
12
+ <ProjectIcon projectId={123} />
13
+ ```
14
+
7
15
  ## Component props
8
16
 
9
17
  <!-- props:start -->
10
18
  ```ts
11
19
  {
12
20
  /**
13
- * Project ID used to generate a deterministic color
21
+ * Project ID used to generate a deterministic color.
22
+ * Accepts either the numeric shortId or a string entity ID.
14
23
  */
15
24
  projectId?: number | string;
25
+
16
26
  /**
17
- * Additional class
27
+ * Additional CSS class applied to the root element.
18
28
  */
19
29
  className?: string;
30
+
20
31
  /**
21
- * Entity type label (defaults to "project")
32
+ * Entity type label. Defaults to "project".
22
33
  */
23
34
  type?: string;
35
+
24
36
  /**
25
- * Size of the SVG icon inside the container in pixels
37
+ * Size of the SVG icon inside the container in pixels.
26
38
  * @default 16
27
39
  */
28
40
  size?: number;
41
+
29
42
  /**
30
- * Size of the icon container in pixels
43
+ * Size of the icon container (the colored square) in pixels.
31
44
  * @default 24
32
45
  */
33
46
  refugeSize?: number;
47
+
34
48
  /**
35
- * Whether to render a colored background behind the icon
49
+ * Whether to render a colored background behind the icon.
36
50
  * @default true
37
51
  */
38
52
  withBackground?: boolean;
53
+
39
54
  /**
40
- * Border radius variant of the icon container
55
+ * Border radius variant of the icon container.
41
56
  * @default 'm'
42
57
  */
43
58
  radius?: 'm' | 'l';
59
+
44
60
  /**
45
- * Alpha (opacity) of the icon color
61
+ * Alpha (opacity) of the icon foreground color.
46
62
  * @default 1
47
63
  */
48
64
  colorAlpha?: number;
49
65
  }
50
66
  ```
51
67
  <!-- props:end -->
68
+
69
+ ---
70
+
71
+ ## Examples
72
+
73
+ ### Default
74
+
75
+ ```tsx
76
+ import {ProjectIcon} from '@weavix/tracker-components';
77
+
78
+ <ProjectIcon projectId={123} />
79
+ ```
80
+
81
+ ### Without background
82
+
83
+ ```tsx
84
+ // Renders only the colored SVG, no background square
85
+ <ProjectIcon projectId={123} withBackground={false} />
86
+ ```
87
+
88
+ ### Custom size
89
+
90
+ ```tsx
91
+ <ProjectIcon projectId={123} size={20} refugeSize={32} />
92
+ ```
93
+
94
+ ### Large radius
95
+
96
+ ```tsx
97
+ <ProjectIcon projectId={123} radius="l" />
98
+ ```
99
+
100
+ ### Alongside project name
101
+
102
+ ```tsx
103
+ import {ProjectIcon} from '@weavix/tracker-components';
104
+
105
+ export const ProjectLabel = ({project}: {project: {shortId: number; name: string}}) => (
106
+ <div style={{display: 'flex', alignItems: 'center', gap: 8}}>
107
+ <ProjectIcon projectId={project.shortId} />
108
+ <span>{project.name}</span>
109
+ </div>
110
+ );
111
+ ```
112
+
113
+ ### Multiple projects (each gets a unique color)
114
+
115
+ ```tsx
116
+ const projects = [
117
+ {shortId: 1, name: 'Backend API'},
118
+ {shortId: 2, name: 'Mobile App'},
119
+ {shortId: 3, name: 'Design System'},
120
+ ];
121
+
122
+ projects.map((p) => (
123
+ <div key={p.shortId} style={{display: 'flex', alignItems: 'center', gap: 8}}>
124
+ <ProjectIcon projectId={p.shortId} />
125
+ <span>{p.name}</span>
126
+ </div>
127
+ ));
128
+ ```
129
+
130
+ ### Rendering from a Tracker API response
131
+
132
+ The Tracker entities search endpoint returns `shortId` as a number — pass it directly:
133
+
134
+ ```tsx
135
+ import React, {useEffect, useState} from 'react';
136
+ import {ProjectIcon} from '@weavix/tracker-components';
137
+ import type {MetaEntityV2} from '@weavix/tracker-api-types';
138
+
139
+ interface Props {
140
+ trackerApi: TrackerApi;
141
+ }
142
+
143
+ export const ProjectList: React.FC<Props> = ({trackerApi}) => {
144
+ const [projects, setProjects] = useState<MetaEntityV2[]>([]);
145
+
146
+ useEffect(() => {
147
+ trackerApi.v3
148
+ .post['/entities/{entityType}/_search']({
149
+ pathParams: {entityType: 'project'},
150
+ queryParams: {fields: ['summary'], page: 1, perPage: 20},
151
+ bodyParams: {filter: {}, filterTypes: ['ALL'], input: ''},
152
+ })
153
+ .then((result) => setProjects(result.data));
154
+ }, [trackerApi]);
155
+
156
+ return (
157
+ <ul>
158
+ {projects.map((project) => (
159
+ <li key={project.id} style={{display: 'flex', alignItems: 'center', gap: 8}}>
160
+ <ProjectIcon projectId={project.shortId} />
161
+ <span>{project.fields?.summary?.value ?? project.id}</span>
162
+ </li>
163
+ ))}
164
+ </ul>
165
+ );
166
+ };
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Notes
172
+
173
+ - `withBackground` defaults to `true` for projects (vs `false` for [`PortfolioIcon`](../PortfolioIcon/README.md)).
174
+ - Pass `projectId` as the **numeric `shortId`** from the Tracker API for consistent color generation. String IDs are also accepted.
175
+ - To render the correct icon for an arbitrary meta entity type, use [`MetaEntityIcon`](../MetaEntityIcon/README.md) instead.
@@ -100,6 +100,16 @@ type MetaEntityV2 = {
100
100
 
101
101
  `MetaEntitiesSelect` is data-agnostic — it accepts items via the `getItems` prop. You are responsible for fetching data from the Tracker API and passing it to the component.
102
102
 
103
+ Requires the `"tracker:entities:write"` permission in your plugin's `manifest.json`:
104
+
105
+ ```json
106
+ {
107
+ "permissions": {
108
+ "data": ["tracker:entities:write"]
109
+ }
110
+ }
111
+ ```
112
+
103
113
  ### Getting projects
104
114
 
105
115
  Use the entities search endpoint to fetch projects (or other entity types):
@@ -235,6 +235,24 @@ type TrackerSelectItem = {
235
235
 
236
236
  `PluginFieldsSelect` is data-agnostic — it accepts fields via the `fields` prop. You are responsible for fetching data from the Tracker API and passing it to the component.
237
237
 
238
+ Add the required permissions to your plugin's `manifest.json`:
239
+
240
+ ```json
241
+ {
242
+ "permissions": {
243
+ "data": [
244
+ "tracker:fields:read",
245
+ "tracker:fields:write",
246
+ "tracker:queues:read"
247
+ ]
248
+ }
249
+ }
250
+ ```
251
+
252
+ - `tracker:fields:read` — required for `GET /fields` and `POST /fields/_suggest`
253
+ - `tracker:fields:write` — required if your plugin writes field values back to Tracker
254
+ - `tracker:queues:read` — required when using `localFields: true` with queue keys (Flow 3)
255
+
238
256
  There are three fetch flows depending on whether the user has typed anything and whether local (queue-specific) fields are needed.
239
257
 
240
258
  ### Flow 1 — Initial load (no user input)
@@ -178,6 +178,16 @@ type QueueSuggestItem = {
178
178
 
179
179
  `QueueSelect` is data-agnostic — it accepts queues via the `queues` prop and search results via the `onSearch` callback. You are responsible for fetching data from the Tracker API and passing it to the component.
180
180
 
181
+ Requires the `"tracker:queues:read"` permission in your plugin's `manifest.json`:
182
+
183
+ ```json
184
+ {
185
+ "permissions": {
186
+ "data": ["tracker:queues:read"]
187
+ }
188
+ }
189
+ ```
190
+
181
191
  There are two fetch flows depending on whether the user has typed anything in the search filter.
182
192
 
183
193
  ### Flow 1 — Initial load (no user input)
@@ -0,0 +1,205 @@
1
+ # TrackerDisclosure
2
+
3
+ <!-- description:start -->
4
+ A collapsible section component with Tracker-specific styling. Extends Gravity UI [`Disclosure`](https://gravity-ui.com/components/disclosure) with a configurable chevron arrow (position and size), an optional toolbar slot rendered alongside the summary, and BEM class name hooks for fine-grained styling.
5
+ <!-- description:end -->
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import {TrackerDisclosure} from '@weavix/tracker-components';
11
+
12
+ <TrackerDisclosure summary="Section title" defaultExpanded>
13
+ Content goes here
14
+ </TrackerDisclosure>
15
+ ```
16
+
17
+ ## Component props
18
+
19
+ <!-- props:start -->
20
+ ```ts
21
+ {
22
+ /**
23
+ * Content shown in the clickable summary / header row.
24
+ * Accepts a string or any ReactNode.
25
+ */
26
+ summary?: ReactNode;
27
+
28
+ /**
29
+ * Content rendered inside the collapsible body.
30
+ */
31
+ children?: ReactNode;
32
+
33
+ /**
34
+ * Whether the section is expanded on first render (uncontrolled).
35
+ * @default false
36
+ */
37
+ defaultExpanded?: boolean;
38
+
39
+ /**
40
+ * Controlled expanded state. When provided, you must also handle onUpdate.
41
+ */
42
+ expanded?: boolean;
43
+
44
+ /**
45
+ * Callback fired when the expanded state changes.
46
+ */
47
+ onUpdate?: (expanded: boolean) => void;
48
+
49
+ /**
50
+ * Whether the disclosure is disabled (non-interactive).
51
+ * @default false
52
+ */
53
+ disabled?: boolean;
54
+
55
+ /**
56
+ * Position of the chevron arrow relative to the summary text.
57
+ * @default 'end'
58
+ */
59
+ arrowPosition?: 'start' | 'end';
60
+
61
+ /**
62
+ * Size of the chevron arrow icon in pixels.
63
+ * @default 20
64
+ */
65
+ arrowSize?: number;
66
+
67
+ /**
68
+ * Optional ReactNode rendered to the right of the toggle button inside the
69
+ * summary row. Useful for action buttons (edit, delete, add, …).
70
+ */
71
+ toolbar?: ReactNode;
72
+
73
+ /**
74
+ * Additional CSS class applied to the root element.
75
+ */
76
+ className?: string;
77
+
78
+ /**
79
+ * Additional CSS class applied to the summary row wrapper.
80
+ */
81
+ summaryClassName?: string;
82
+
83
+ /**
84
+ * Additional CSS class applied to the toggle `<button>` element.
85
+ */
86
+ toggleClassName?: string;
87
+
88
+ /**
89
+ * Additional CSS class applied to the chevron arrow icon.
90
+ */
91
+ arrowClassName?: string;
92
+ }
93
+ ```
94
+ <!-- props:end -->
95
+
96
+ > **Note:** `TrackerDisclosure` accepts all props from Gravity UI [`DisclosureProps`](https://gravity-ui.com/components/disclosure) in addition to the Tracker-specific ones listed above.
97
+
98
+ ---
99
+
100
+ ## Examples
101
+
102
+ ### Basic (uncontrolled)
103
+
104
+ ```tsx
105
+ import {TrackerDisclosure} from '@weavix/tracker-components';
106
+
107
+ <TrackerDisclosure summary="Sprint details" defaultExpanded>
108
+ <p>Sprint 42 — 14 issues, 3 in progress.</p>
109
+ </TrackerDisclosure>
110
+ ```
111
+
112
+ ---
113
+
114
+ ### Controlled
115
+
116
+ ```tsx
117
+ import React, {useState} from 'react';
118
+ import {TrackerDisclosure} from '@weavix/tracker-components';
119
+
120
+ export const ControlledExample = () => {
121
+ const [expanded, setExpanded] = useState(false);
122
+
123
+ return (
124
+ <TrackerDisclosure
125
+ summary="Filters"
126
+ expanded={expanded}
127
+ onUpdate={setExpanded}
128
+ >
129
+ <p>Filter content here.</p>
130
+ </TrackerDisclosure>
131
+ );
132
+ };
133
+ ```
134
+
135
+ ---
136
+
137
+ ### Arrow at start
138
+
139
+ ```tsx
140
+ <TrackerDisclosure summary="Description" arrowPosition="start">
141
+ <p>Content with the chevron on the left.</p>
142
+ </TrackerDisclosure>
143
+ ```
144
+
145
+ ---
146
+
147
+ ### With toolbar
148
+
149
+ The `toolbar` prop renders additional content (e.g. action buttons) to the right of the toggle button, inside the summary row but outside the toggle click area.
150
+
151
+ ```tsx
152
+ import {Button} from '@gravity-ui/uikit';
153
+ import {TrackerDisclosure} from '@weavix/tracker-components';
154
+
155
+ <TrackerDisclosure
156
+ summary="Attachments"
157
+ toolbar={
158
+ <Button size="s" view="flat" onClick={() => openUploadDialog()}>
159
+ Add file
160
+ </Button>
161
+ }
162
+ >
163
+ <AttachmentList />
164
+ </TrackerDisclosure>
165
+ ```
166
+
167
+ ---
168
+
169
+ ### Disabled
170
+
171
+ ```tsx
172
+ <TrackerDisclosure summary="Locked section" disabled defaultExpanded>
173
+ <p>This section cannot be collapsed by the user.</p>
174
+ </TrackerDisclosure>
175
+ ```
176
+
177
+ ---
178
+
179
+ ### Multiple sections (accordion-like)
180
+
181
+ ```tsx
182
+ import React, {useState} from 'react';
183
+ import {TrackerDisclosure} from '@weavix/tracker-components';
184
+
185
+ const SECTIONS = ['Details', 'Attachments', 'Comments'];
186
+
187
+ export const AccordionExample = () => {
188
+ const [openIndex, setOpenIndex] = useState<number | null>(0);
189
+
190
+ return (
191
+ <>
192
+ {SECTIONS.map((title, i) => (
193
+ <TrackerDisclosure
194
+ key={title}
195
+ summary={title}
196
+ expanded={openIndex === i}
197
+ onUpdate={(open) => setOpenIndex(open ? i : null)}
198
+ >
199
+ <p>Content for {title}</p>
200
+ </TrackerDisclosure>
201
+ ))}
202
+ </>
203
+ );
204
+ };
205
+ ```
@@ -8,8 +8,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
8
8
  export var EntityIcon = /*#__PURE__*/memo(function (_ref) {
9
9
  var data = _ref.data,
10
10
  className = _ref.className,
11
- _ref$id = _ref.id,
12
- id = _ref$id === void 0 ? ' ' : _ref$id,
11
+ id = _ref.id,
13
12
  type = _ref.type,
14
13
  _ref$refugeSize = _ref.refugeSize,
15
14
  refugeSize = _ref$refugeSize === void 0 ? 24 : _ref$refugeSize,
@@ -25,7 +24,7 @@ export var EntityIcon = /*#__PURE__*/memo(function (_ref) {
25
24
  colorAlpha = _ref$colorAlpha === void 0 ? 1 : _ref$colorAlpha;
26
25
  var theme = useThemeType();
27
26
  var randomColor = useColorGenerator({
28
- seed: String(id),
27
+ seed: id !== undefined ? String(id) : ' ',
29
28
  theme: theme
30
29
  });
31
30
  var color = "rgba(".concat(randomColor.rgb.r, ",").concat(randomColor.rgb.g, ",").concat(randomColor.rgb.b, ",").concat(colorAlpha, ")");
@@ -1 +1 @@
1
- {"version":3,"names":["React","memo","useColorGenerator","useThemeType","IconRefuge","cnEntityIcon","jsx","_jsx","EntityIcon","_ref","data","className","_ref$id","id","type","_ref$refugeSize","refugeSize","_ref$size","size","_ref$alpha","alpha","_ref$withBackground","withBackground","_ref$radius","radius","_ref$colorAlpha","colorAlpha","theme","randomColor","seed","String","color","concat","rgb","r","g","b","backgroundColor","containerStyle","background","containerClassName","displayName"],"sources":["EntityIcon.tsx"],"sourcesContent":["import type {FC} from 'react';\nimport React, {memo} from 'react';\n\nimport {type IconData, useColorGenerator, useThemeType} from '@gravity-ui/uikit';\n\nimport {IconRefuge} from '../IconRefuge';\n\nimport {cnEntityIcon} from './EntityIcon.classname';\n\nimport './EntityIcon.scss';\n\nexport type EntityIconProps = {\n data: IconData;\n className?: string;\n id?: number | string;\n type?: string;\n refugeSize?: number;\n size?: number;\n alpha?: number;\n withBackground?: boolean;\n radius?: 'm' | 'l';\n colorAlpha?: number;\n};\n\nexport const EntityIcon: FC<EntityIconProps> = memo(\n ({\n data,\n className,\n id = ' ',\n type,\n refugeSize = 24,\n size = 16,\n alpha = 0.15,\n withBackground = true,\n radius = 'm',\n colorAlpha = 1,\n }) => {\n const theme = useThemeType();\n\n const randomColor = useColorGenerator({seed: String(id), theme});\n\n const color = `rgba(${randomColor.rgb.r},${randomColor.rgb.g},${randomColor.rgb.b},${colorAlpha})`;\n const backgroundColor = `rgba(${randomColor.rgb.r},${randomColor.rgb.g},${randomColor.rgb.b},${alpha})`;\n\n const containerStyle: {\n background?: string;\n color?: string;\n } = {};\n\n if (withBackground) {\n containerStyle.background = backgroundColor;\n containerStyle.color = color;\n } else {\n containerStyle.color = backgroundColor;\n }\n\n return (\n <IconRefuge\n data={data}\n containerClassName={cnEntityIcon({type, radius}, className)}\n refugeSize={refugeSize}\n size={size}\n containerStyle={containerStyle}\n />\n );\n },\n);\n\nEntityIcon.displayName = 'EntityIcon';\n"],"mappings":";AACA,OAAOA,KAAK,IAAGC,IAAI,QAAO,OAAO;AAEjC,SAAuBC,iBAAiB,EAAEC,YAAY,QAAO,mBAAmB;AAEhF,SAAQC,UAAU;AAElB,SAAQC,YAAY;AAEpB;AAA2B,SAAAC,GAAA,IAAAC,IAAA;AAe3B,OAAO,IAAMC,UAA+B,gBAAGP,IAAI,CAC/C,UAAAQ,IAAA,EAWM;EAAA,IAVFC,IAAI,GAAAD,IAAA,CAAJC,IAAI;IACJC,SAAS,GAAAF,IAAA,CAATE,SAAS;IAAAC,OAAA,GAAAH,IAAA,CACTI,EAAE;IAAFA,EAAE,GAAAD,OAAA,cAAG,GAAG,GAAAA,OAAA;IACRE,IAAI,GAAAL,IAAA,CAAJK,IAAI;IAAAC,eAAA,GAAAN,IAAA,CACJO,UAAU;IAAVA,UAAU,GAAAD,eAAA,cAAG,EAAE,GAAAA,eAAA;IAAAE,SAAA,GAAAR,IAAA,CACfS,IAAI;IAAJA,IAAI,GAAAD,SAAA,cAAG,EAAE,GAAAA,SAAA;IAAAE,UAAA,GAAAV,IAAA,CACTW,KAAK;IAALA,KAAK,GAAAD,UAAA,cAAG,IAAI,GAAAA,UAAA;IAAAE,mBAAA,GAAAZ,IAAA,CACZa,cAAc;IAAdA,cAAc,GAAAD,mBAAA,cAAG,IAAI,GAAAA,mBAAA;IAAAE,WAAA,GAAAd,IAAA,CACrBe,MAAM;IAANA,MAAM,GAAAD,WAAA,cAAG,GAAG,GAAAA,WAAA;IAAAE,eAAA,GAAAhB,IAAA,CACZiB,UAAU;IAAVA,UAAU,GAAAD,eAAA,cAAG,CAAC,GAAAA,eAAA;EAEd,IAAME,KAAK,GAAGxB,YAAY,CAAC,CAAC;EAE5B,IAAMyB,WAAW,GAAG1B,iBAAiB,CAAC;IAAC2B,IAAI,EAAEC,MAAM,CAACjB,EAAE,CAAC;IAAEc,KAAK,EAALA;EAAK,CAAC,CAAC;EAEhE,IAAMI,KAAK,WAAAC,MAAA,CAAWJ,WAAW,CAACK,GAAG,CAACC,CAAC,OAAAF,MAAA,CAAIJ,WAAW,CAACK,GAAG,CAACE,CAAC,OAAAH,MAAA,CAAIJ,WAAW,CAACK,GAAG,CAACG,CAAC,OAAAJ,MAAA,CAAIN,UAAU,MAAG;EAClG,IAAMW,eAAe,WAAAL,MAAA,CAAWJ,WAAW,CAACK,GAAG,CAACC,CAAC,OAAAF,MAAA,CAAIJ,WAAW,CAACK,GAAG,CAACE,CAAC,OAAAH,MAAA,CAAIJ,WAAW,CAACK,GAAG,CAACG,CAAC,OAAAJ,MAAA,CAAIZ,KAAK,MAAG;EAEvG,IAAMkB,cAGL,GAAG,CAAC,CAAC;EAEN,IAAIhB,cAAc,EAAE;IAChBgB,cAAc,CAACC,UAAU,GAAGF,eAAe;IAC3CC,cAAc,CAACP,KAAK,GAAGA,KAAK;EAChC,CAAC,MAAM;IACHO,cAAc,CAACP,KAAK,GAAGM,eAAe;EAC1C;EAEA,oBACI9B,IAAA,CAACH,UAAU;IACPM,IAAI,EAAEA,IAAK;IACX8B,kBAAkB,EAAEnC,YAAY,CAAC;MAACS,IAAI,EAAJA,IAAI;MAAEU,MAAM,EAANA;IAAM,CAAC,EAAEb,SAAS,CAAE;IAC5DK,UAAU,EAAEA,UAAW;IACvBE,IAAI,EAAEA,IAAK;IACXoB,cAAc,EAAEA;EAAe,CAClC,CAAC;AAEV,CACJ,CAAC;AAED9B,UAAU,CAACiC,WAAW,GAAG,YAAY","ignoreList":[]}
1
+ {"version":3,"names":["React","memo","useColorGenerator","useThemeType","IconRefuge","cnEntityIcon","jsx","_jsx","EntityIcon","_ref","data","className","id","type","_ref$refugeSize","refugeSize","_ref$size","size","_ref$alpha","alpha","_ref$withBackground","withBackground","_ref$radius","radius","_ref$colorAlpha","colorAlpha","theme","randomColor","seed","undefined","String","color","concat","rgb","r","g","b","backgroundColor","containerStyle","background","containerClassName","displayName"],"sources":["EntityIcon.tsx"],"sourcesContent":["import type {FC} from 'react';\nimport React, {memo} from 'react';\n\nimport {type IconData, useColorGenerator, useThemeType} from '@gravity-ui/uikit';\n\nimport {IconRefuge} from '../IconRefuge';\n\nimport {cnEntityIcon} from './EntityIcon.classname';\n\nimport './EntityIcon.scss';\n\nexport type EntityIconProps = {\n data: IconData;\n className?: string;\n id?: number | string;\n type?: string;\n refugeSize?: number;\n size?: number;\n alpha?: number;\n withBackground?: boolean;\n radius?: 'm' | 'l';\n colorAlpha?: number;\n};\n\nexport const EntityIcon: FC<EntityIconProps> = memo(\n ({\n data,\n className,\n id,\n type,\n refugeSize = 24,\n size = 16,\n alpha = 0.15,\n withBackground = true,\n radius = 'm',\n colorAlpha = 1,\n }) => {\n const theme = useThemeType();\n\n const randomColor = useColorGenerator({seed: id !== undefined ? String(id) : ' ', theme});\n\n const color = `rgba(${randomColor.rgb.r},${randomColor.rgb.g},${randomColor.rgb.b},${colorAlpha})`;\n const backgroundColor = `rgba(${randomColor.rgb.r},${randomColor.rgb.g},${randomColor.rgb.b},${alpha})`;\n\n const containerStyle: {\n background?: string;\n color?: string;\n } = {};\n\n if (withBackground) {\n containerStyle.background = backgroundColor;\n containerStyle.color = color;\n } else {\n containerStyle.color = backgroundColor;\n }\n\n return (\n <IconRefuge\n data={data}\n containerClassName={cnEntityIcon({type, radius}, className)}\n refugeSize={refugeSize}\n size={size}\n containerStyle={containerStyle}\n />\n );\n },\n);\n\nEntityIcon.displayName = 'EntityIcon';\n"],"mappings":";AACA,OAAOA,KAAK,IAAGC,IAAI,QAAO,OAAO;AAEjC,SAAuBC,iBAAiB,EAAEC,YAAY,QAAO,mBAAmB;AAEhF,SAAQC,UAAU;AAElB,SAAQC,YAAY;AAEpB;AAA2B,SAAAC,GAAA,IAAAC,IAAA;AAe3B,OAAO,IAAMC,UAA+B,gBAAGP,IAAI,CAC/C,UAAAQ,IAAA,EAWM;EAAA,IAVFC,IAAI,GAAAD,IAAA,CAAJC,IAAI;IACJC,SAAS,GAAAF,IAAA,CAATE,SAAS;IACTC,EAAE,GAAAH,IAAA,CAAFG,EAAE;IACFC,IAAI,GAAAJ,IAAA,CAAJI,IAAI;IAAAC,eAAA,GAAAL,IAAA,CACJM,UAAU;IAAVA,UAAU,GAAAD,eAAA,cAAG,EAAE,GAAAA,eAAA;IAAAE,SAAA,GAAAP,IAAA,CACfQ,IAAI;IAAJA,IAAI,GAAAD,SAAA,cAAG,EAAE,GAAAA,SAAA;IAAAE,UAAA,GAAAT,IAAA,CACTU,KAAK;IAALA,KAAK,GAAAD,UAAA,cAAG,IAAI,GAAAA,UAAA;IAAAE,mBAAA,GAAAX,IAAA,CACZY,cAAc;IAAdA,cAAc,GAAAD,mBAAA,cAAG,IAAI,GAAAA,mBAAA;IAAAE,WAAA,GAAAb,IAAA,CACrBc,MAAM;IAANA,MAAM,GAAAD,WAAA,cAAG,GAAG,GAAAA,WAAA;IAAAE,eAAA,GAAAf,IAAA,CACZgB,UAAU;IAAVA,UAAU,GAAAD,eAAA,cAAG,CAAC,GAAAA,eAAA;EAEd,IAAME,KAAK,GAAGvB,YAAY,CAAC,CAAC;EAE5B,IAAMwB,WAAW,GAAGzB,iBAAiB,CAAC;IAAC0B,IAAI,EAAEhB,EAAE,KAAKiB,SAAS,GAAGC,MAAM,CAAClB,EAAE,CAAC,GAAG,GAAG;IAAEc,KAAK,EAALA;EAAK,CAAC,CAAC;EAEzF,IAAMK,KAAK,WAAAC,MAAA,CAAWL,WAAW,CAACM,GAAG,CAACC,CAAC,OAAAF,MAAA,CAAIL,WAAW,CAACM,GAAG,CAACE,CAAC,OAAAH,MAAA,CAAIL,WAAW,CAACM,GAAG,CAACG,CAAC,OAAAJ,MAAA,CAAIP,UAAU,MAAG;EAClG,IAAMY,eAAe,WAAAL,MAAA,CAAWL,WAAW,CAACM,GAAG,CAACC,CAAC,OAAAF,MAAA,CAAIL,WAAW,CAACM,GAAG,CAACE,CAAC,OAAAH,MAAA,CAAIL,WAAW,CAACM,GAAG,CAACG,CAAC,OAAAJ,MAAA,CAAIb,KAAK,MAAG;EAEvG,IAAMmB,cAGL,GAAG,CAAC,CAAC;EAEN,IAAIjB,cAAc,EAAE;IAChBiB,cAAc,CAACC,UAAU,GAAGF,eAAe;IAC3CC,cAAc,CAACP,KAAK,GAAGA,KAAK;EAChC,CAAC,MAAM;IACHO,cAAc,CAACP,KAAK,GAAGM,eAAe;EAC1C;EAEA,oBACI9B,IAAA,CAACH,UAAU;IACPM,IAAI,EAAEA,IAAK;IACX8B,kBAAkB,EAAEnC,YAAY,CAAC;MAACQ,IAAI,EAAJA,IAAI;MAAEU,MAAM,EAANA;IAAM,CAAC,EAAEZ,SAAS,CAAE;IAC5DI,UAAU,EAAEA,UAAW;IACvBE,IAAI,EAAEA,IAAK;IACXqB,cAAc,EAAEA;EAAe,CAClC,CAAC;AAEV,CACJ,CAAC;AAED9B,UAAU,CAACiC,WAAW,GAAG,YAAY","ignoreList":[]}
@@ -0,0 +1,123 @@
1
+ # GoalIcon
2
+
3
+ <!-- description:start -->
4
+ Displays a colored icon for a Tracker goal entity. The icon color is deterministically derived from the `goalId`, so the same goal always renders the same color across sessions and environments.
5
+ <!-- description:end -->
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import {GoalIcon} from '@weavix/tracker-components';
11
+
12
+ <GoalIcon goalId={456} />
13
+ ```
14
+
15
+ ## Component props
16
+
17
+ <!-- props:start -->
18
+ ```ts
19
+ {
20
+ /**
21
+ * Goal ID used to generate a deterministic color.
22
+ * Accepts either a numeric shortId or a string entity ID.
23
+ */
24
+ goalId: number | string;
25
+
26
+ /**
27
+ * Additional CSS class applied to the root element.
28
+ */
29
+ className?: string;
30
+
31
+ /**
32
+ * Entity type label. Defaults to "goal".
33
+ */
34
+ type?: string;
35
+
36
+ /**
37
+ * Size of the SVG icon inside the container in pixels.
38
+ * @default 16
39
+ */
40
+ size?: number;
41
+
42
+ /**
43
+ * Size of the icon container (the colored square) in pixels.
44
+ * @default 24
45
+ */
46
+ refugeSize?: number;
47
+
48
+ /**
49
+ * Whether to render a colored background behind the icon.
50
+ * @default true
51
+ */
52
+ withBackground?: boolean;
53
+
54
+ /**
55
+ * Border radius variant of the icon container.
56
+ * @default 'm'
57
+ */
58
+ radius?: 'm' | 'l';
59
+
60
+ /**
61
+ * Alpha (opacity) of the icon foreground color.
62
+ * @default 1
63
+ */
64
+ colorAlpha?: number;
65
+ }
66
+ ```
67
+ <!-- props:end -->
68
+
69
+ ---
70
+
71
+ ## Examples
72
+
73
+ ### Default
74
+
75
+ ```tsx
76
+ import {GoalIcon} from '@weavix/tracker-components';
77
+
78
+ <GoalIcon goalId={456} />
79
+ ```
80
+
81
+ ### Custom size
82
+
83
+ ```tsx
84
+ // Larger icon container with a larger SVG inside
85
+ <GoalIcon goalId={456} size={20} refugeSize={32} />
86
+ ```
87
+
88
+ ### Without background
89
+
90
+ ```tsx
91
+ // Renders only the colored SVG, no background square
92
+ <GoalIcon goalId={456} withBackground={false} />
93
+ ```
94
+
95
+ ### Alongside goal name
96
+
97
+ ```tsx
98
+ import {GoalIcon} from '@weavix/tracker-components';
99
+
100
+ export const GoalLabel = ({goal}: {goal: {shortId: number; summary: string}}) => (
101
+ <div style={{display: 'flex', alignItems: 'center', gap: 8}}>
102
+ <GoalIcon goalId={goal.shortId} />
103
+ <span>{goal.summary}</span>
104
+ </div>
105
+ );
106
+ ```
107
+
108
+ ### Multiple goals (each gets a unique color)
109
+
110
+ ```tsx
111
+ const goals = [
112
+ {shortId: 1, summary: 'Increase retention'},
113
+ {shortId: 2, summary: 'Launch mobile app'},
114
+ {shortId: 3, summary: 'Reduce churn'},
115
+ ];
116
+
117
+ goals.map((g) => (
118
+ <div key={g.shortId} style={{display: 'flex', alignItems: 'center', gap: 8}}>
119
+ <GoalIcon goalId={g.shortId} />
120
+ <span>{g.summary}</span>
121
+ </div>
122
+ ));
123
+ ```
@@ -1 +1 @@
1
- {"version":3,"names":["block","cnIconRefuge"],"sources":["IconRefuge.classname.tsx"],"sourcesContent":["import {block} from '~/src/components/cn';\n\nexport const cnIconRefuge = block('icon-refuge');\n"],"mappings":"AAAA,SAAQA,KAAK;AAEb,OAAO,IAAMC,YAAY,GAAGD,KAAK,CAAC,aAAa,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["block","cnIconRefuge"],"sources":["IconRefuge.classname.ts"],"sourcesContent":["import {block} from '~/src/components/cn';\n\nexport const cnIconRefuge = block('icon-refuge');\n"],"mappings":"AAAA,SAAQA,KAAK;AAEb,OAAO,IAAMC,YAAY,GAAGD,KAAK,CAAC,aAAa,CAAC","ignoreList":[]}
@@ -3,9 +3,6 @@
3
3
  align-items: center;
4
4
  justify-content: center;
5
5
  }
6
- .tpc-icon-refuge_inline {
7
- display: inline-flex;
8
- }
9
6
  .tpc-icon-refuge__hidden-label {
10
7
  position: absolute;
11
8
  overflow: hidden;
@@ -1 +1 @@
1
- {"version":3,"sources":["IconRefuge.scss"],"names":[],"mappings":"AAAA;EACI,oBAAA;EACA,mBAAA;EACA,uBAAA;AACJ;AACI;EACI,oBAAA;AACR;AAEI;EACI,kBAAA;EAEA,gBAAA;EACA,mBAAA;EAEA,UAAA;EACA,WAAA;EACA,UAAA;EACA,YAAA;EAEA,mBAAA;AAHR","file":"IconRefuge.css","sourcesContent":[".tpc-icon-refuge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n &_inline {\n display: inline-flex;\n }\n\n &__hidden-label {\n position: absolute;\n\n overflow: hidden;\n clip: rect(0 0 0 0);\n\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n\n white-space: nowrap;\n }\n}\n"],"sourceRoot":""}
1
+ {"version":3,"sources":["IconRefuge.scss"],"names":[],"mappings":"AAAA;EACI,oBAAA;EACA,mBAAA;EACA,uBAAA;AACJ;AACI;EACI,kBAAA;EAEA,gBAAA;EACA,mBAAA;EAEA,UAAA;EACA,WAAA;EACA,UAAA;EACA,YAAA;EAEA,mBAAA;AAFR","file":"IconRefuge.css","sourcesContent":[".tpc-icon-refuge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n &__hidden-label {\n position: absolute;\n\n overflow: hidden;\n clip: rect(0 0 0 0);\n\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n\n white-space: nowrap;\n }\n}\n"],"sourceRoot":""}
@@ -3,10 +3,6 @@
3
3
  align-items: center;
4
4
  justify-content: center;
5
5
 
6
- &_inline {
7
- display: inline-flex;
8
- }
9
-
10
6
  &__hidden-label {
11
7
  position: absolute;
12
8