Skip to content

Commit 536bd22

Browse files
Merge pull request #64 from infinum/release/6.0.2
6.0.2
2 parents c81b9cb + dd00c23 commit 536bd22

File tree

11 files changed

+128
-110
lines changed

11 files changed

+128
-110
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file.
33

44
This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/).
55

6+
## [6.0.2] - 2025-11-21
7+
- `Container` updates
8+
- Added `centered` prop to vertically center content (you might need to make inner content flex grow or fill width).
9+
- Added `lessSpaceStart` and `lessSpaceEnd` props for more control over horizontal padding.
10+
- Tweaked accent shade.
11+
- `ContainerGroup` now supports a `label` prop to label the group.
12+
- Fixed `DraggableList` item z-index while dragging.
13+
- `Menu` now supports the `hidden` prop to programmatically hide the component.
14+
- `OptionsPanelHeader` has set title margins to prevent overrides.
15+
- `RichLabel` doesn't have the `noColor` prop anymore - it's now the default behavior.
16+
- Improved `SmartImage` analysis reliability.
17+
- Updated dependencies.
18+
619
## [6.0.1] - 2025-11-14
720
- Fix output issue with `ContainerGroup` passed components.
821

@@ -501,6 +514,7 @@ Co-authored with @piqusy
501514
- Initial release
502515

503516
[Unreleased]: https://github.com/infinum/eightshift-ui-components/compare/master...HEAD
517+
[6.0.2]: https://github.com/infinum/eightshift-ui-components/compare/6.0.1...6.0.2
504518
[6.0.1]: https://github.com/infinum/eightshift-ui-components/compare/6.0.0...6.0.1
505519
[6.0.0]: https://github.com/infinum/eightshift-ui-components/compare/5.6.1...6.0.0
506520
[5.6.1]: https://github.com/infinum/eightshift-ui-components/compare/5.6.0...5.6.1

bun.lock

Lines changed: 29 additions & 69 deletions
Large diffs are not rendered by default.

lib/components/base-control/container.jsx

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { cloneElement, forwardRef } from 'react';
1212
* @property {boolean} [props.isChild] - If `true`, applies child-specific styling for nested containers.
1313
* @property {boolean} [props.compact] - If `true`, the vertical padding is reduced for a more compact appearance.
1414
* @property {boolean} [props.standalone] - If `true`, the border radius is not adjusted automatically, based on neightboring containers.
15+
* @property {boolean} [props.centered] - If `true`, the content is centered vertically.
16+
* @property {boolean} [props.lessSpaceStart] - If `true`, space on the start (left) is reduced. Useful for symmetric components.
17+
* @property {boolean} [props.lessSpaceEnd] - If `true`, space on the end (right) is reduced. For example, use with text fields, or taller items.
1518
* @property {string|JSX.Element} [props.as] - The HTML element or React component to render as the container.
1619
*
1720
* @preserve
@@ -36,15 +39,15 @@ import { cloneElement, forwardRef } from 'react';
3639
* @preserve
3740
*/
3841
export const Container = forwardRef((props, ref) => {
39-
const { className, children, as, hidden, accent, elevated, primary, isChild, compact, standalone, horizontal, ...rest } = props;
42+
const { className, children, as, hidden, accent, elevated, primary, isChild, compact, standalone, horizontal, centered, lessSpaceStart, lessSpaceEnd, ...rest } = props;
4043

4144
const ComponentToRender = as || 'div';
4245

4346
if (hidden) {
4447
return null;
4548
}
4649

47-
const containerClasses = cva([' es:inset-ring es:px-2.5', className], {
50+
const containerClasses = cva(['es:inset-ring', className], {
4851
variants: {
4952
elevated: {
5053
true: 'es:inset-shadow-sm es:shadow-sm es:shadow-black/5',
@@ -56,6 +59,17 @@ export const Container = forwardRef((props, ref) => {
5659
false: 'es:py-2 es:min-h-13',
5760
true: 'es:py-1 es:min-h-9',
5861
},
62+
centered: {
63+
true: 'es:flex es:items-center',
64+
},
65+
lessSpaceStart: {
66+
true: 'es:pl-2',
67+
false: 'es:pl-3',
68+
},
69+
lessSpaceEnd: {
70+
true: 'es:pr-2',
71+
false: 'es:pr-3',
72+
},
5973
},
6074
compoundVariants: [
6175
{
@@ -111,7 +125,7 @@ export const Container = forwardRef((props, ref) => {
111125
{
112126
accent: true,
113127
elevated: false,
114-
class: 'es:bg-surface-50 es:inset-ring-surface-100',
128+
class: 'es:bg-surface-100/80 es:inset-ring-surface-200 es:text-accent-900',
115129
},
116130
{
117131
accent: false,
@@ -132,14 +146,17 @@ export const Container = forwardRef((props, ref) => {
132146
compact: false,
133147
standalone: false,
134148
horizontal: false,
149+
centered: false,
150+
lessSpaceStart: false,
151+
lessSpaceEnd: false,
135152
},
136153
});
137154

138155
return (
139156
<ComponentToRender
140157
{...rest}
141158
ref={ref}
142-
className={containerClasses({ accent, elevated, primary, isChild, compact, horizontal, standalone })}
159+
className={containerClasses({ accent, elevated, primary, isChild, compact, horizontal, standalone, centered, lessSpaceStart, lessSpaceEnd })}
143160
>
144161
{children}
145162
</ComponentToRender>
@@ -151,6 +168,8 @@ Container.displayName = 'Container';
151168
/**
152169
* @typedef {Object} ContainerGroupProps
153170
* @property {string} [className] - Classes to pass to the container group.
171+
* @property {string} [wrapClassName] - Classes to pass to the control wrapper - only if label is set.
172+
* @property {string|JSX.Element} [label] - Label to show above the container group.
154173
* @property {boolean} [hidden] - If `true`, the component is not rendered.
155174
* @property {boolean} [horizontal] - If `true`, the component uses a horizontal orientation.
156175
* @property {string|JSX.Element} [as] - The HTML element or React component to render as the container group.
@@ -178,7 +197,7 @@ Container.displayName = 'Container';
178197
* @preserve
179198
*/
180199
export const ContainerGroup = forwardRef((props, ref) => {
181-
const { className, children, as, hidden, horizontal, ...rest } = props;
200+
const { className, children, as, hidden, horizontal, label, wrapClassName, ...rest } = props;
182201

183202
const ComponentToRender = as || 'div';
184203

@@ -188,7 +207,7 @@ export const ContainerGroup = forwardRef((props, ref) => {
188207

189208
const processedChildren = Array.isArray(children)
190209
? children.reduce((acc, child, index) => {
191-
if (child.type.displayName === 'Container') {
210+
if (child?.type?.displayName === 'Container') {
192211
return [
193212
...acc,
194213
cloneElement(child, {
@@ -202,7 +221,11 @@ export const ContainerGroup = forwardRef((props, ref) => {
202221
}, [])
203222
: children;
204223

205-
return (
224+
if (!processedChildren || processedChildren?.length < 1) {
225+
return null;
226+
}
227+
228+
const inner = (
206229
<ComponentToRender
207230
{...rest}
208231
ref={ref}
@@ -211,6 +234,21 @@ export const ContainerGroup = forwardRef((props, ref) => {
211234
{processedChildren}
212235
</ComponentToRender>
213236
);
237+
238+
if (!label) {
239+
return inner;
240+
}
241+
242+
if (Array.isArray(inner?.props?.children) && !inner?.props?.children?.filter(Boolean)?.length) {
243+
return null;
244+
}
245+
246+
return (
247+
<div className={wrapClassName}>
248+
<span className='es:ml-1 es:mb-1 es:inline-block es:text-12 es:font-variation-["wdth"_125,"wght"_400] es:text-surface-500'>{label}</span>
249+
{inner}
250+
</div>
251+
);
214252
});
215253

216254
ContainerGroup.displayName = 'ContainerGroup';

lib/components/draggable-list/draggable-list.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const DraggableList = (props) => {
135135
key={key}
136136
accent={isDragged || isSelected}
137137
elevated={isDragged || isSelected}
138-
className={clsx('es:list-none es:m-0!', itemClassName)}
138+
className={clsx('es:list-none es:m-0!', isDragged && 'es:z-99999', itemClassName)}
139139
data-selected={isDragged || isSelected || props?.style?.position === 'fixed'}
140140
{...rest}
141141
>

lib/components/menu/menu.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ export const MenuSeparator = ({ className }) => {
213213
* @param {boolean} [props.danger] - If `true`, the item appearance is tweaked to indicate a dangerous action.
214214
* @param {boolean} [props.primary] - If `true`, the item appearance is tweaked to indicate a primary action.
215215
* @param {string} [props.className] - Classes to pass to the menu item.
216+
* @param {string} [props.aria-label] - Aria label for the menu item. Defaults to the children text or 'Menu item' if children is not a string.
217+
* @param {boolean} [props.hidden] - If `true`, the component is not rendered.
216218
*
217219
* @returns {JSX.Element} The MenuItem component.
218220
*
@@ -236,8 +238,13 @@ export const MenuItem = (props) => {
236238
primary,
237239
className,
238240
'aria-label': ariaLabel = typeof children === 'string' ? children : __('Menu item', 'eightshift-ui-components'),
241+
hidden,
239242
} = props;
240243

244+
if (hidden) {
245+
return null;
246+
}
247+
241248
let itemIcon = icon;
242249

243250
if (checked === true) {

lib/components/options-panel/options-panel.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,17 @@ export const OptionsPanelHeader = ({ children, sticky, title, className, actions
122122

123123
return (
124124
<div className={clsx('es:space-y-2.5', limitWidth && 'es:max-w-2xl', sticky && 'es:sticky es:top-0 es:z-10 es:bg-white', className)}>
125-
<div className='es:flex es:flex-wrap es:items-center es:justify-between es:gap-x-8 es:gap-y-4'>
125+
<div className='es:flex es:flex-wrap es:items-center es:justify-between es:gap-x-8 es:gap-y-4 es:mb-10'>
126126
<Heading
127-
className='es:text-3xl es:text-surface-800 es:font-variation-["wdth"_180,"YTLC"_540,"wght"_300]'
127+
className='es:text-3xl es:text-surface-800 es:font-variation-["wdth"_180,"YTLC"_540,"wght"_300] es:m-0!'
128128
level={level}
129129
>
130130
{title}
131131
</Heading>
132132

133133
<div className='es:flex es:items-center es:gap-2'>{actions}</div>
134134
</div>
135+
135136
{children}
136137
</div>
137138
);

lib/components/rich-label/rich-label.jsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { clsx } from 'clsx/lite';
1717
* @param {boolean} [props.fullWidth=false] - If `true`, the component will take up as much space as it can.
1818
* @param {boolean} [props.contentsOnly] - If `true`, only the label (/icon/subtitle) will be rendered, without any wrapping elements. Useful if you want to provide your own layout.
1919
* @param {boolean} [props.hidden] - If `true`, the component is not rendered.
20-
* @param {boolean} [props.noColor] - If `true`, colors on text won't be set, opacity will be used instead.
2120
* @param {boolean} [props.fullSizeSubtitle] - If `true`, the subtitle is the same size as the label.
2221
* @param {boolean} [props.inlineSubtitle] - If `true`, the subtitle is shown after the label instead of below it.
2322
*
@@ -45,7 +44,6 @@ export const RichLabel = (props) => {
4544
fullWidth = false,
4645
contentsOnly,
4746
hidden,
48-
noColor,
4947
fullSizeSubtitle,
5048
inlineSubtitle,
5149
} = props;
@@ -59,18 +57,16 @@ export const RichLabel = (props) => {
5957
if (contentsOnly) {
6058
return (
6159
<>
62-
{icon && <span className={clsx('es:icon:size-5', !noColor && 'es:text-secondary-500', iconClassName)}>{icon}</span>}
63-
{label && <span className={clsx('es:text-balance', !noColor && 'es:text-secondary-800', labelClassName)}>{label}</span>}
64-
{subtitle && <span className={clsx('es:text-balance es:text-xs es:not-contrast-more:opacity-65', !noColor && 'es:text-secondary-700', subtitleClassName)}>{subtitle}</span>}
60+
{icon && <span className={clsx('es:icon:size-5 es:not-contrast-more:opacity-85', iconClassName)}>{icon}</span>}
61+
{label && <span className={clsx('es:text-balance', labelClassName)}>{label}</span>}
62+
{subtitle && <span className={clsx('es:text-balance es:text-xs es:not-contrast-more:opacity-65', subtitleClassName)}>{subtitle}</span>}
6563
</>
6664
);
6765
}
6866

6967
return (
70-
<ComponentToRender
71-
className={clsx('es:flex es:items-center es:gap-1.75 es:text-sm', !noColor && 'es:text-secondary-700 es:any-icon:text-secondary-500', fullWidth && 'es:grow', className)}
72-
>
73-
{icon && <span className={clsx('es:icon:size-5 es:shrink-0', noColor && 'es:not-contrast-more:opacity-80', iconClassName)}>{icon}</span>}
68+
<ComponentToRender className={clsx('es:flex es:items-center es:gap-1.75 es:text-sm', fullWidth && 'es:grow', className)}>
69+
{icon && <span className={clsx('es:icon:size-5 es:shrink-0', 'es:not-contrast-more:opacity-85', iconClassName)}>{icon}</span>}
7470

7571
{(label || subtitle) && (
7672
<div className={clsx('es:flex es:items-start es:text-balance es:text-start', inlineSubtitle ? 'es:gap-1.5' : 'es:flex-col', labelSubtitleWrapClassName)}>

lib/components/smart-image/smart-image.jsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,6 @@ export const SmartImage = (props) => {
8888

8989
const classFetchProps = { isLoaded, hasAnalysed, isTransparent, dominantColors, isDark, transparencyInfo };
9090

91-
if (analysisData) {
92-
delete imageProps.analysisData;
93-
}
94-
9591
const imageElement = (
9692
<img
9793
decoding='async'
@@ -122,13 +118,15 @@ export const SmartImage = (props) => {
122118
if (analysisData) {
123119
const { isDark: dark, dominantColors: colors, isTransparent: transparent, transparencyInfo } = analysisData;
124120

125-
setIsDark(dark);
126-
setDominantColors(colors);
127-
setIsTransparent(transparent);
128-
setTransparencyInfo(transparencyInfo);
129-
setHasAnalysed(true);
121+
if (dark !== undefined && colors !== undefined && transparent !== undefined && transparencyInfo !== undefined) {
122+
setIsDark(dark);
123+
setDominantColors(colors);
124+
setIsTransparent(transparent);
125+
setTransparencyInfo(transparencyInfo);
126+
setHasAnalysed(true);
130127

131-
return;
128+
return;
129+
}
132130
}
133131

134132
// Cache results in localstorage.

lib/components/smart-image/worker-inline.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/utilities/general.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ export const analyzeImage = (image, rawSettings) => {
8080
skipTransparencyCheck = !['png', 'webp', 'gif', 'tiff', 'svg', 'avif'].includes(fileExtension);
8181
}
8282

83-
const imageWidth = image.naturalWidth;
84-
const imageHeight = image.naturalHeight;
83+
let imageWidth = image.naturalWidth || image.width;
84+
let imageHeight = image.naturalHeight || image.height;
85+
86+
if (!image.complete) {
87+
throw new Error('Image not fully loaded');
88+
}
8589

8690
if (!imageWidth || !imageHeight) {
8791
return false;

0 commit comments

Comments
 (0)