@eyesee/ui-components 0.3.0 → 0.4.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/README.md CHANGED
@@ -38,15 +38,15 @@ import '@eyesee/ui-components/styles';
38
38
 
39
39
  Add `alias--main` and exactly one profile class to a common ancestor element — ideally `<body>`. The profile class controls the visual theme of the library:
40
40
 
41
- | Class | Use for |
42
- |---|---|
43
- | `alias--main` | Always required |
44
- | `mapped--internal` | Internal EyeSee tools |
41
+ | Class | Use for |
42
+ | ------------------ | ---------------------------- |
43
+ | `alias--main` | Always required |
44
+ | `mapped--internal` | Internal EyeSee tools |
45
45
  | `mapped--external` | External-facing applications |
46
46
 
47
47
  ```html
48
48
  <body class="alias--main mapped--internal">
49
- ...
49
+ ...
50
50
  </body>
51
51
  ```
52
52
 
@@ -82,60 +82,60 @@ import { PlusIcon, DeleteIcon, EditIcon } from '@eyesee/ui-components/icons';
82
82
 
83
83
  ### Layout & Feedback
84
84
 
85
- | Component | Description |
86
- |---|---|
87
- | `Modal` | Dialog overlay with title, description, icon, and footer slots |
88
- | `Infobox` | Inline status message with icon, title, and body text |
89
- | `ProgressBar` | Determinate progress indicator with optional label and percentage |
90
- | `ExpandableItem` | Collapsible `<details>`-based section with a summary title |
85
+ | Component | Description |
86
+ | ---------------- | ----------------------------------------------------------------- |
87
+ | `Modal` | Dialog overlay with title, description, icon, and footer slots |
88
+ | `Infobox` | Inline status message with icon, title, and body text |
89
+ | `ProgressBar` | Determinate progress indicator with optional label and percentage |
90
+ | `ExpandableItem` | Collapsible `<details>`-based section with a summary title |
91
91
 
92
92
  ### Navigation & Selection
93
93
 
94
- | Component | Description |
95
- |---|---|
96
- | `Tabs` | Tab group managing selection state; wraps `Tab` |
97
- | `Tab` | Individual tab rendered as a button or span |
94
+ | Component | Description |
95
+ | --------- | ----------------------------------------------- |
96
+ | `Tabs` | Tab group managing selection state; wraps `Tab` |
97
+ | `Tab` | Individual tab rendered as a button or span |
98
98
 
99
99
  ### Inputs & Form Fields
100
100
 
101
- | Component | Description |
102
- |---|---|
103
- | `InputField` | Single-line text input with label, icons, error, and info text |
104
- | `TextField` | Multi-line textarea with label, action buttons, error, and info text |
105
- | `SelectField` | Dropdown select with label and error support |
106
- | `Checkbox` | Checkbox with label, indeterminate state, and size variants |
107
- | `Radio` | Radio button with label and size variants |
108
- | `Toggle` | Switch (checkbox) with label and size variants |
101
+ | Component | Description |
102
+ | ------------- | -------------------------------------------------------------------- |
103
+ | `InputField` | Single-line text input with label, icons, error, and info text |
104
+ | `TextField` | Multi-line textarea with label, action buttons, error, and info text |
105
+ | `SelectField` | Dropdown select with label and error support |
106
+ | `Checkbox` | Checkbox with label, indeterminate state, and size variants |
107
+ | `Radio` | Radio button with label and size variants |
108
+ | `Toggle` | Switch (checkbox) with label and size variants |
109
109
 
110
110
  ### Buttons & Actions
111
111
 
112
- | Component | Description |
113
- |---|---|
114
- | `Button` | Primary action button with optional dropdown menu |
112
+ | Component | Description |
113
+ | -------------- | ------------------------------------------------------ |
114
+ | `Button` | Primary action button with optional dropdown menu |
115
115
  | `DropdownList` | Scrollable menu container; used by `Button` internally |
116
116
 
117
117
  ### Display
118
118
 
119
- | Component | Description |
120
- |---|---|
121
- | `Avatar` | User avatar as initials or photo with size and corner variants |
122
- | `Badge` | Numeric or text badge in rectangle or pill shape |
123
- | `Pill` | Status indicator with icon and color/status variants |
124
- | `Tag` | Label chip with optional dismiss button |
125
- | `ListItem` | Row with title, description, trailing action icons, and slots |
126
- | `FileType` | File type icon indicator |
127
- | `ImagePlaceholder` | Placeholder shown while an image loads |
128
- | `ImageWithData` | Image with overlaid metadata |
129
- | `TextHoverable` | Text that reveals additional content on hover |
130
- | `Tooltip` | Floating label anchored to a trigger element |
131
- | `EllipsisTooltip` | Tooltip that only activates when text is truncated |
119
+ | Component | Description |
120
+ | ------------------ | -------------------------------------------------------------- |
121
+ | `Avatar` | User avatar as initials or photo with size and corner variants |
122
+ | `Badge` | Numeric or text badge in rectangle or pill shape |
123
+ | `Pill` | Status indicator with icon and color/status variants |
124
+ | `Tag` | Label chip with optional dismiss button |
125
+ | `ListItem` | Row with title, description, trailing action icons, and slots |
126
+ | `FileType` | File type icon indicator |
127
+ | `ImagePlaceholder` | Placeholder shown while an image loads |
128
+ | `ImageWithData` | Image with overlaid metadata |
129
+ | `TextHoverable` | Text that reveals additional content on hover |
130
+ | `Tooltip` | Floating label anchored to a trigger element |
131
+ | `EllipsisTooltip` | Tooltip that only activates when text is truncated |
132
132
 
133
133
  ### Notifications
134
134
 
135
- | Component | Description |
136
- |---|---|
137
- | `Notifications` | Toast notification card (title, text, type, actions) |
138
- | `useNotifications` | Hook for triggering toasts imperatively |
135
+ | Component | Description |
136
+ | ------------------ | ---------------------------------------------------- |
137
+ | `Notifications` | Toast notification card (title, text, type, actions) |
138
+ | `useNotifications` | Hook for triggering toasts imperatively |
139
139
 
140
140
  Notifications require a single `<ToastContainer>` from `react-toastify` in your app:
141
141
 
@@ -156,7 +156,11 @@ function SomeComponent() {
156
156
  const { notify } = useNotifications();
157
157
 
158
158
  return (
159
- <button onClick={() => notify({ title: 'Saved!', text: 'Your changes were saved.', type: 'success' })}>
159
+ <button
160
+ onClick={() =>
161
+ notify({ title: 'Saved!', text: 'Your changes were saved.', type: 'success' })
162
+ }
163
+ >
160
164
  Save
161
165
  </button>
162
166
  );
@@ -197,7 +201,7 @@ const options = [
197
201
  variant="primary"
198
202
  options={options}
199
203
  onClick={(option) => console.log(option.value)}
200
- />
204
+ />;
201
205
  ```
202
206
 
203
207
  ### Form fields
@@ -242,11 +246,56 @@ import { Modal, Button } from '@eyesee/ui-components';
242
246
  onClose={() => setIsOpen(false)}
243
247
  footer={
244
248
  <>
245
- <Button text="Cancel" variant="neutral" type="outline" onClick={() => setIsOpen(false)} />
249
+ <Button
250
+ text="Cancel"
251
+ variant="neutral"
252
+ type="outline"
253
+ onClick={() => setIsOpen(false)}
254
+ />
246
255
  <Button text="Delete" variant="destructive" onClick={handleDelete} />
247
256
  </>
248
257
  }
249
- />
258
+ />;
259
+ ```
260
+
261
+ Use `bodyLayout="fixed"` when the footer can grow (e.g. error Infobox) while content scrolls inside the body. Default body height is `480px` (`360px` below 1536px); override with `bodyHeight` like `maxWidth`. Pass direct children (no single wrapper around all content). Static sections use `flexShrink: 0`; scrollable sections use `flex: 1`, `minHeight: 0`, and `overflowY: 'auto'`. For multi-section layouts, use a flex parent with `display: 'flex'`, `flex: 1`, `minHeight: 0`, and `overflow: 'hidden'`. See the `FixedBodyLayout` Storybook story.
262
+
263
+ ```jsx
264
+ import { Modal, Button, Infobox, InputField, ListItem } from '@eyesee/ui-components';
265
+
266
+ <Modal
267
+ visible={isOpen}
268
+ title="Create shelf"
269
+ bodyLayout="fixed"
270
+ bodyHeight={{ default: '520px', mediaQuery: '400px' }}
271
+ maxWidth={{ default: '720px', mediaQuery: '640px' }}
272
+ onClose={() => setIsOpen(false)}
273
+ footer={
274
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
275
+ {uploadError && (
276
+ <Infobox status="error" title="Upload failed" text={uploadError} fullWidth />
277
+ )}
278
+ <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
279
+ <Button
280
+ text="Cancel"
281
+ variant="neutral"
282
+ type="outline"
283
+ onClick={() => setIsOpen(false)}
284
+ />
285
+ <Button text="Create" onClick={handleCreate} />
286
+ </div>
287
+ </div>
288
+ }
289
+ >
290
+ <div style={{ flexShrink: 0 }}>
291
+ <InputField label="Shelf name" value={name} onChange={handleNameChange} />
292
+ </div>
293
+ <div style={{ flex: 1, minHeight: 0, overflowY: 'auto' }}>
294
+ {items.map((item) => (
295
+ <ListItem key={item.id} title={item.title} />
296
+ ))}
297
+ </div>
298
+ </Modal>;
250
299
  ```
251
300
 
252
301
  ### ListItem with trailing actions
@@ -263,7 +312,7 @@ import { EditIcon, DeleteIcon } from '@eyesee/ui-components/icons';
263
312
  { type: 'edit', icon: <EditIcon />, onClick: (item) => handleEdit(item) },
264
313
  { type: 'delete', icon: <DeleteIcon />, onClick: (item) => handleDelete(item) },
265
314
  ]}
266
- />
315
+ />;
267
316
  ```
268
317
 
269
318
  ---
@@ -4,14 +4,7 @@ import c from "./Badge.module.scss.js";
4
4
  const g = ({ label: r, shape: o = "rectangle", state: e = "neutral" }) => {
5
5
  if (r == null || r === "") return null;
6
6
  const { backgroundColor: t, color: n, borderRadius: s } = a(o, e);
7
- return /* @__PURE__ */ l(
8
- "span",
9
- {
10
- className: c.badge,
11
- style: { backgroundColor: t, color: n, borderRadius: s },
12
- children: r
13
- }
14
- );
7
+ return /* @__PURE__ */ l("span", { className: c.badge, style: { backgroundColor: t, color: n, borderRadius: s }, children: r });
15
8
  };
16
9
  export {
17
10
  g as Badge
@@ -43,103 +43,93 @@ const to = ({
43
43
  return null;
44
44
  }
45
45
  }, f = !a || t === x.outside ? "8px" : "8px 8px 0 0", w = Number(C), L = n(e.contentText, e.contentTextClamped), A = { WebkitLineClamp: w };
46
- return /* @__PURE__ */ c(
47
- "div",
48
- {
49
- className: n(
50
- e.imagePlaceholder,
51
- m && e.disabled
52
- ),
53
- children: [
54
- /* @__PURE__ */ c(
55
- "div",
56
- {
57
- className: n(
58
- e.imageArea,
59
- s === "checkbox" && e.clickable,
60
- s === "checkbox" && !i && e.checkboxSelectable
61
- ),
62
- style: { borderRadius: f },
63
- onClick: s === "checkbox" && !i && !m ? p : void 0,
64
- children: [
65
- /* @__PURE__ */ o(
66
- "div",
46
+ return /* @__PURE__ */ c("div", { className: n(e.imagePlaceholder, m && e.disabled), children: [
47
+ /* @__PURE__ */ c(
48
+ "div",
49
+ {
50
+ className: n(
51
+ e.imageArea,
52
+ s === "checkbox" && e.clickable,
53
+ s === "checkbox" && !i && e.checkboxSelectable
54
+ ),
55
+ style: { borderRadius: f },
56
+ onClick: s === "checkbox" && !i && !m ? p : void 0,
57
+ children: [
58
+ /* @__PURE__ */ o(
59
+ "div",
60
+ {
61
+ className: e.imageFrame,
62
+ style: { aspectRatio: N[v], borderRadius: f },
63
+ children: i ? /* @__PURE__ */ o("div", { className: e.imageError, children: /* @__PURE__ */ o(
64
+ _,
67
65
  {
68
- className: e.imageFrame,
69
- style: { aspectRatio: N[v], borderRadius: f },
70
- children: i ? /* @__PURE__ */ o("div", { className: e.imageError, children: /* @__PURE__ */ o(
71
- _,
72
- {
73
- iconSize: "l",
74
- strokeColor: "var(--color--icon--disabled--default)"
75
- }
76
- ) }) : /* @__PURE__ */ o(
77
- "img",
78
- {
79
- className: e.image,
80
- src: k,
81
- alt: a,
82
- onError: () => P(!0)
83
- }
84
- )
66
+ iconSize: "l",
67
+ strokeColor: "var(--color--icon--disabled--default)"
85
68
  }
86
- ),
87
- !a && T.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { className: e.tagInImage, children: u() }) : null,
88
- z.has(s) && /* @__PURE__ */ c("div", { className: e.overlay, onClick: (l) => l.stopPropagation(), children: [
89
- s === "checkbox" && !i ? /* @__PURE__ */ o(
90
- y,
91
- {
92
- checked: S,
93
- onChange: p,
94
- size: "lg",
95
- disabled: m
96
- }
97
- ) : null,
98
- s === "close" ? /* @__PURE__ */ o(
99
- X,
100
- {
101
- icon: /* @__PURE__ */ o(B, {}),
102
- size: "xxs",
103
- variant: "fill",
104
- type: "neutral",
105
- onClick: E
106
- }
107
- ) : null
108
- ] })
109
- ]
110
- }
111
- ),
112
- a ? /* @__PURE__ */ c("div", { className: n(e.belowImage, e[t]), children: [
113
- T.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { children: u() }) : null,
114
- /* @__PURE__ */ c("div", { className: e.content, children: [
115
- /* @__PURE__ */ o(
116
- Y,
69
+ ) }) : /* @__PURE__ */ o(
70
+ "img",
71
+ {
72
+ className: e.image,
73
+ src: k,
74
+ alt: a,
75
+ onError: () => P(!0)
76
+ }
77
+ )
78
+ }
79
+ ),
80
+ !a && T.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { className: e.tagInImage, children: u() }) : null,
81
+ z.has(s) && /* @__PURE__ */ c("div", { className: e.overlay, onClick: (l) => l.stopPropagation(), children: [
82
+ s === "checkbox" && !i ? /* @__PURE__ */ o(
83
+ y,
117
84
  {
118
- position: "bottom",
119
- showArrow: !0,
120
- className: L,
121
- wrapperStyle: A,
122
- children: a
85
+ checked: S,
86
+ onChange: p,
87
+ size: "lg",
88
+ disabled: m
123
89
  }
124
- ),
125
- d && /* @__PURE__ */ c("div", { className: e.menu, children: [
126
- /* @__PURE__ */ o("span", { onClick: () => g(!h), children: /* @__PURE__ */ o(j, {}) }),
127
- h && /* @__PURE__ */ o(D, { children: d.map((l) => /* @__PURE__ */ o(
128
- G,
129
- {
130
- title: l == null ? void 0 : l.title,
131
- value: l == null ? void 0 : l.value,
132
- item: l
133
- },
134
- l == null ? void 0 : l.value
135
- )) })
136
- ] })
90
+ ) : null,
91
+ s === "close" ? /* @__PURE__ */ o(
92
+ X,
93
+ {
94
+ icon: /* @__PURE__ */ o(B, {}),
95
+ size: "xxs",
96
+ variant: "fill",
97
+ type: "neutral",
98
+ onClick: E
99
+ }
100
+ ) : null
137
101
  ] })
138
- ] }) : null
139
- ]
140
- },
141
- I
142
- );
102
+ ]
103
+ }
104
+ ),
105
+ a ? /* @__PURE__ */ c("div", { className: n(e.belowImage, e[t]), children: [
106
+ T.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { children: u() }) : null,
107
+ /* @__PURE__ */ c("div", { className: e.content, children: [
108
+ /* @__PURE__ */ o(
109
+ Y,
110
+ {
111
+ position: "bottom",
112
+ showArrow: !0,
113
+ className: L,
114
+ wrapperStyle: A,
115
+ children: a
116
+ }
117
+ ),
118
+ d && /* @__PURE__ */ c("div", { className: e.menu, children: [
119
+ /* @__PURE__ */ o("span", { onClick: () => g(!h), children: /* @__PURE__ */ o(j, {}) }),
120
+ h && /* @__PURE__ */ o(D, { children: d.map((l) => /* @__PURE__ */ o(
121
+ G,
122
+ {
123
+ title: l == null ? void 0 : l.title,
124
+ value: l == null ? void 0 : l.value,
125
+ item: l
126
+ },
127
+ l == null ? void 0 : l.value
128
+ )) })
129
+ ] })
130
+ ] })
131
+ ] }) : null
132
+ ] }, I);
143
133
  };
144
134
  export {
145
135
  to as default
@@ -42,104 +42,94 @@ const ae = ({
42
42
  return null;
43
43
  }
44
44
  }, u = !s || t === x.outside ? "8px" : "8px 8px 0 0", A = Number(S), O = n(e.contentText, e.contentTextClamped), w = { WebkitLineClamp: A }, l = C || m;
45
- return /* @__PURE__ */ a(
46
- "div",
47
- {
48
- className: n(
49
- e.imagePlaceholder,
50
- m && e.disabled
51
- ),
52
- children: [
53
- /* @__PURE__ */ a(
54
- "div",
55
- {
56
- className: n(
57
- e.imageArea,
58
- i === "checkbox" && e.clickable,
59
- i === "checkbox" && !c && e.checkboxSelectable
60
- ),
61
- style: { borderRadius: u },
62
- onClick: i === "checkbox" && !c && !m ? d : void 0,
63
- children: [
64
- /* @__PURE__ */ o(
65
- "div",
45
+ return /* @__PURE__ */ a("div", { className: n(e.imagePlaceholder, m && e.disabled), children: [
46
+ /* @__PURE__ */ a(
47
+ "div",
48
+ {
49
+ className: n(
50
+ e.imageArea,
51
+ i === "checkbox" && e.clickable,
52
+ i === "checkbox" && !c && e.checkboxSelectable
53
+ ),
54
+ style: { borderRadius: u },
55
+ onClick: i === "checkbox" && !c && !m ? d : void 0,
56
+ children: [
57
+ /* @__PURE__ */ o(
58
+ "div",
59
+ {
60
+ className: e.imageFrame,
61
+ style: { aspectRatio: f[I], borderRadius: u },
62
+ children: c ? /* @__PURE__ */ o("div", { className: e.imageError, children: /* @__PURE__ */ o(
63
+ W,
66
64
  {
67
- className: e.imageFrame,
68
- style: { aspectRatio: f[I], borderRadius: u },
69
- children: c ? /* @__PURE__ */ o("div", { className: e.imageError, children: /* @__PURE__ */ o(
70
- W,
71
- {
72
- iconSize: "l",
73
- strokeColor: "var(--color--icon--disabled--default)"
74
- }
75
- ) }) : /* @__PURE__ */ o(
76
- "img",
77
- {
78
- className: e.image,
79
- src: k,
80
- alt: s,
81
- onError: () => y(!0)
82
- }
83
- )
65
+ iconSize: "l",
66
+ strokeColor: "var(--color--icon--disabled--default)"
84
67
  }
85
- ),
86
- !s && b.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { className: e.tagInImage, children: h() }) : null,
87
- B.has(i) && /* @__PURE__ */ a("div", { className: e.overlay, onClick: (L) => L.stopPropagation(), children: [
88
- i === "checkbox" && !c ? /* @__PURE__ */ o(
89
- F,
90
- {
91
- checked: v,
92
- onChange: l ? void 0 : d,
93
- size: "lg",
94
- disabled: l
95
- }
96
- ) : null,
97
- i === "close" ? /* @__PURE__ */ o(
98
- N,
99
- {
100
- icon: /* @__PURE__ */ o(X, {}),
101
- size: "xxs",
102
- variant: "fill",
103
- type: "neutral",
104
- disabled: l,
105
- onClick: l ? void 0 : E
106
- }
107
- ) : null,
108
- i === "icon" && /* @__PURE__ */ o(
109
- N,
110
- {
111
- icon: g,
112
- size: "xs",
113
- variant: "fill",
114
- type: "neutral",
115
- disabled: l,
116
- onClick: l ? void 0 : P
117
- }
118
- )
119
- ] })
120
- ]
121
- }
122
- ),
123
- s ? /* @__PURE__ */ a("div", { className: n(e.belowImage, e[t]), children: [
124
- b.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { children: h() }) : null,
125
- /* @__PURE__ */ a("div", { className: e.content, children: [
126
- /* @__PURE__ */ o(
127
- Y,
68
+ ) }) : /* @__PURE__ */ o(
69
+ "img",
70
+ {
71
+ className: e.image,
72
+ src: k,
73
+ alt: s,
74
+ onError: () => y(!0)
75
+ }
76
+ )
77
+ }
78
+ ),
79
+ !s && b.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { className: e.tagInImage, children: h() }) : null,
80
+ B.has(i) && /* @__PURE__ */ a("div", { className: e.overlay, onClick: (L) => L.stopPropagation(), children: [
81
+ i === "checkbox" && !c ? /* @__PURE__ */ o(
82
+ F,
128
83
  {
129
- position: "bottom",
130
- showArrow: !0,
131
- className: O,
132
- wrapperStyle: w,
133
- children: s
84
+ checked: v,
85
+ onChange: l ? void 0 : d,
86
+ size: "lg",
87
+ disabled: l
134
88
  }
135
- ),
136
- p && /* @__PURE__ */ o("div", { className: e.number, children: p })
89
+ ) : null,
90
+ i === "close" ? /* @__PURE__ */ o(
91
+ N,
92
+ {
93
+ icon: /* @__PURE__ */ o(X, {}),
94
+ size: "xxs",
95
+ variant: "fill",
96
+ type: "neutral",
97
+ disabled: l,
98
+ onClick: l ? void 0 : E
99
+ }
100
+ ) : null,
101
+ i === "icon" && /* @__PURE__ */ o(
102
+ N,
103
+ {
104
+ icon: g,
105
+ size: "xs",
106
+ variant: "fill",
107
+ type: "neutral",
108
+ disabled: l,
109
+ onClick: l ? void 0 : P
110
+ }
111
+ )
137
112
  ] })
138
- ] }) : null
139
- ]
140
- },
141
- T
142
- );
113
+ ]
114
+ }
115
+ ),
116
+ s ? /* @__PURE__ */ a("div", { className: n(e.belowImage, e[t]), children: [
117
+ b.has(r == null ? void 0 : r.type) ? /* @__PURE__ */ o("div", { children: h() }) : null,
118
+ /* @__PURE__ */ a("div", { className: e.content, children: [
119
+ /* @__PURE__ */ o(
120
+ Y,
121
+ {
122
+ position: "bottom",
123
+ showArrow: !0,
124
+ className: O,
125
+ wrapperStyle: w,
126
+ children: s
127
+ }
128
+ ),
129
+ p && /* @__PURE__ */ o("div", { className: e.number, children: p })
130
+ ] })
131
+ ] }) : null
132
+ ] }, T);
143
133
  };
144
134
  export {
145
135
  ae as default