@seekora-ai/ui-sdk-react 0.2.21 → 0.2.23
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/components/Facets.d.ts +4 -0
- package/dist/components/Facets.d.ts.map +1 -1
- package/dist/components/Facets.js +12 -15
- package/dist/components/HitsPerPage.d.ts.map +1 -1
- package/dist/components/HitsPerPage.js +21 -11
- package/dist/components/SortBy.d.ts.map +1 -1
- package/dist/components/SortBy.js +8 -11
- package/dist/components/primitives/CustomSelect.d.ts +40 -0
- package/dist/components/primitives/CustomSelect.d.ts.map +1 -0
- package/dist/components/primitives/CustomSelect.js +196 -0
- package/dist/components/primitives/ImageZoom.d.ts.map +1 -1
- package/dist/components/primitives/ImageZoom.js +37 -16
- package/dist/components/primitives/VariantSelector.d.ts.map +1 -1
- package/dist/components/primitives/VariantSelector.js +17 -24
- package/dist/components/primitives/index.d.ts +1 -0
- package/dist/components/primitives/index.d.ts.map +1 -1
- package/dist/components/primitives/index.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +46 -2
- package/dist/src/index.esm.js +287 -78
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +287 -77
- package/dist/src/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -107,6 +107,10 @@ export interface FacetsProps {
|
|
|
107
107
|
useFiltersApi?: boolean;
|
|
108
108
|
/** Fields that should use disjunctive (OR) faceting (only with useFiltersApi) */
|
|
109
109
|
disjunctiveFacets?: string[];
|
|
110
|
+
/** Hide facets that have no selectable values (default: true) */
|
|
111
|
+
hideEmptyFacets?: boolean;
|
|
112
|
+
/** Fields that should start collapsed (collapsible variant only). Overrides defaultCollapsed for listed fields. */
|
|
113
|
+
defaultCollapsedFields?: string[];
|
|
110
114
|
}
|
|
111
115
|
export declare const Facets: React.FC<FacetsProps>;
|
|
112
116
|
//# sourceMappingURL=Facets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Facets.d.ts","sourceRoot":"","sources":["../../src/components/Facets.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Facets.d.ts","sourceRoot":"","sources":["../../src/components/Facets.tsx"],"names":[],"mappings":"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"}
|
|
@@ -66,7 +66,7 @@ const CSS_VAR_DEFAULTS = {
|
|
|
66
66
|
// ---------------------------------------------------------------------------
|
|
67
67
|
// Component
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
|
-
export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, renderFacet, renderFacetItem, maxItems = 10, showMore = true, className, style, theme: customTheme, variant = 'checkbox', searchable = false, showCounts = true, colorMap, defaultCollapsed = false, size = 'medium', facetRanges, useFiltersApi = false, disjunctiveFacets, }) => {
|
|
69
|
+
export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, renderFacet, renderFacetItem, maxItems = 10, showMore = true, className, style, theme: customTheme, variant = 'checkbox', searchable = false, showCounts = true, colorMap, defaultCollapsed = false, size = 'medium', facetRanges, useFiltersApi = false, disjunctiveFacets, hideEmptyFacets = true, defaultCollapsedFields, }) => {
|
|
70
70
|
const { theme } = useSearchContext();
|
|
71
71
|
const { results: stateResults, refinements, addRefinement, removeRefinement } = useSearchState();
|
|
72
72
|
const facetsTheme = customTheme || {};
|
|
@@ -143,7 +143,10 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
143
143
|
});
|
|
144
144
|
return extracted;
|
|
145
145
|
};
|
|
146
|
-
const
|
|
146
|
+
const rawFacetList = extractFacets();
|
|
147
|
+
const facets = hideEmptyFacets
|
|
148
|
+
? rawFacetList.filter(f => f.items.length > 0 || f.stats != null)
|
|
149
|
+
: rawFacetList;
|
|
147
150
|
// -------------------------------------------------------------------
|
|
148
151
|
// Handlers
|
|
149
152
|
// -------------------------------------------------------------------
|
|
@@ -180,17 +183,21 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
180
183
|
}));
|
|
181
184
|
};
|
|
182
185
|
/** For collapsible variant — determine if a facet group is open. */
|
|
186
|
+
const isFieldDefaultCollapsed = (field) => {
|
|
187
|
+
if (defaultCollapsedFields?.includes(field))
|
|
188
|
+
return true;
|
|
189
|
+
return defaultCollapsed;
|
|
190
|
+
};
|
|
183
191
|
const isFacetGroupOpen = (field) => {
|
|
184
192
|
if (field in expandedFacets) {
|
|
185
193
|
return expandedFacets[field];
|
|
186
194
|
}
|
|
187
|
-
|
|
188
|
-
return !defaultCollapsed;
|
|
195
|
+
return !isFieldDefaultCollapsed(field);
|
|
189
196
|
};
|
|
190
197
|
const toggleCollapsible = (field) => {
|
|
191
198
|
setExpandedFacets((prev) => ({
|
|
192
199
|
...prev,
|
|
193
|
-
[field]: !(prev[field] ?? !
|
|
200
|
+
[field]: !(prev[field] ?? !isFieldDefaultCollapsed(field)),
|
|
194
201
|
}));
|
|
195
202
|
};
|
|
196
203
|
const getSearchTerm = (field) => searchTerms[field] || '';
|
|
@@ -436,8 +443,6 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
436
443
|
borderRadius: typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium,
|
|
437
444
|
} },
|
|
438
445
|
React.createElement("h3", { className: facetsTheme.facetTitle, style: {
|
|
439
|
-
fontSize: theme.typography.fontSize.large,
|
|
440
|
-
fontWeight: 'bold',
|
|
441
446
|
margin: 0,
|
|
442
447
|
marginBottom: theme.spacing.medium,
|
|
443
448
|
color: theme.colors.text,
|
|
@@ -461,8 +466,6 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
461
466
|
borderRadius: typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium,
|
|
462
467
|
} },
|
|
463
468
|
React.createElement("h3", { className: facetsTheme.facetTitle, style: {
|
|
464
|
-
fontSize: theme.typography.fontSize.large,
|
|
465
|
-
fontWeight: 'bold',
|
|
466
469
|
margin: 0,
|
|
467
470
|
marginBottom: theme.spacing.medium,
|
|
468
471
|
color: theme.colors.text,
|
|
@@ -508,8 +511,6 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
508
511
|
textAlign: 'left',
|
|
509
512
|
} },
|
|
510
513
|
React.createElement("span", { className: facetsTheme.facetTitle, style: {
|
|
511
|
-
fontSize: theme.typography.fontSize.large,
|
|
512
|
-
fontWeight: 'bold',
|
|
513
514
|
color: theme.colors.text,
|
|
514
515
|
flex: 1,
|
|
515
516
|
} }, facet.label || facet.field),
|
|
@@ -628,8 +629,6 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
628
629
|
borderRadius: typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium,
|
|
629
630
|
} },
|
|
630
631
|
React.createElement("h3", { className: facetsTheme.facetTitle, style: {
|
|
631
|
-
fontSize: theme.typography.fontSize.large,
|
|
632
|
-
fontWeight: 'bold',
|
|
633
632
|
margin: 0,
|
|
634
633
|
marginBottom: theme.spacing.medium,
|
|
635
634
|
color: theme.colors.text,
|
|
@@ -710,8 +709,6 @@ export const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange
|
|
|
710
709
|
} },
|
|
711
710
|
React.createElement("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: theme.spacing.small } },
|
|
712
711
|
React.createElement("h3", { className: facetsTheme.facetTitle, style: {
|
|
713
|
-
fontSize: theme.typography.fontSize.large,
|
|
714
|
-
fontWeight: 'bold',
|
|
715
712
|
margin: 0,
|
|
716
713
|
color: theme.colors.text,
|
|
717
714
|
} }, facet.label || facet.field),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HitsPerPage.d.ts","sourceRoot":"","sources":["../../src/components/HitsPerPage.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"HitsPerPage.d.ts","sourceRoot":"","sources":["../../src/components/HitsPerPage.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAiC,MAAM,OAAO,CAAC;AAMtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,0CAA0C;IAC1C,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,wCAAwC;IACxC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;QAC5D,KAAK,EAAE,eAAe,EAAE,CAAC;KAC1B,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA8GlD,CAAC"}
|
|
@@ -8,6 +8,7 @@ import React, { useEffect } from 'react';
|
|
|
8
8
|
import { useSearchContext } from './SearchProvider';
|
|
9
9
|
import { useSearchState } from '../hooks/useSearchState';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
|
+
import { CustomSelect } from './primitives/CustomSelect';
|
|
11
12
|
export const HitsPerPage = ({ items, onHitsPerPageChange, renderSelect, className, style, theme: customTheme, label = 'Show', syncWithState = true, }) => {
|
|
12
13
|
const { theme } = useSearchContext();
|
|
13
14
|
const { itemsPerPage, setPage } = useSearchState();
|
|
@@ -56,17 +57,26 @@ export const HitsPerPage = ({ items, onHitsPerPageChange, renderSelect, classNam
|
|
|
56
57
|
fontSize: theme.typography.fontSize.medium,
|
|
57
58
|
color: theme.colors.text,
|
|
58
59
|
} }, label)),
|
|
59
|
-
React.createElement(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
React.createElement(CustomSelect, { value: String(value), onChange: (val) => {
|
|
61
|
+
const newValue = parseInt(val, 10);
|
|
62
|
+
setInternalValue(newValue);
|
|
63
|
+
if (syncWithState) {
|
|
64
|
+
stateManager.setItemsPerPage(newValue, false);
|
|
65
|
+
setPage(1, true);
|
|
66
|
+
}
|
|
67
|
+
if (onHitsPerPageChange) {
|
|
68
|
+
onHitsPerPageChange(newValue);
|
|
69
|
+
}
|
|
70
|
+
}, options: items.map((item) => ({
|
|
71
|
+
value: String(item.value),
|
|
72
|
+
label: item.label,
|
|
73
|
+
})), "aria-label": "Results per page", className: hitsPerPageTheme.select, style: {
|
|
74
|
+
'--seekora-select-font-size': theme.typography.fontSize.medium,
|
|
75
|
+
'--seekora-select-border': theme.colors.border,
|
|
76
|
+
'--seekora-select-radius': typeof theme.borderRadius === 'string'
|
|
65
77
|
? theme.borderRadius
|
|
66
78
|
: theme.borderRadius.medium,
|
|
67
|
-
|
|
68
|
-
color: theme.colors.text,
|
|
69
|
-
|
|
70
|
-
outline: 'none',
|
|
71
|
-
}, "aria-label": "Results per page" }, items.map((item) => (React.createElement("option", { key: item.value, value: item.value, className: hitsPerPageTheme.option }, item.label))))));
|
|
79
|
+
'--seekora-select-bg': theme.colors.background,
|
|
80
|
+
'--seekora-select-color': theme.colors.text,
|
|
81
|
+
} })));
|
|
72
82
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SortBy.d.ts","sourceRoot":"","sources":["../../src/components/SortBy.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAwC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SortBy.d.ts","sourceRoot":"","sources":["../../src/components/SortBy.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAwC,MAAM,OAAO,CAAC;AAU7D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,cAAc,GAAG,aAAa,CAAC;AACxE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,0BAA0B;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,wEAAwE;IACxE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;QAC5D,OAAO,EAAE,UAAU,EAAE,CAAC;KACvB,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AA8BD,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAuRxC,CAAC"}
|
|
@@ -19,6 +19,7 @@ import React, { useCallback, useEffect, useId } from 'react';
|
|
|
19
19
|
import { useSearchContext } from './SearchProvider';
|
|
20
20
|
import { useSearchState } from '../hooks/useSearchState';
|
|
21
21
|
import { clsx } from 'clsx';
|
|
22
|
+
import { CustomSelect } from './primitives/CustomSelect';
|
|
22
23
|
// ---------------------------------------------------------------------------
|
|
23
24
|
// Helpers
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
@@ -105,18 +106,14 @@ export const SortBy = ({ options, value: valueProp, defaultValue, onSortChange,
|
|
|
105
106
|
}
|
|
106
107
|
return (React.createElement("div", { className: clsx(sortByTheme.container, className), style: { ...cssVarStyle, ...style } },
|
|
107
108
|
labelElement,
|
|
108
|
-
React.createElement(
|
|
109
|
-
padding,
|
|
110
|
-
paddingRight: theme.spacing.medium,
|
|
111
|
-
fontSize,
|
|
112
|
-
border: '1px solid var(--seekora-sort-border)',
|
|
113
|
-
borderRadius,
|
|
114
|
-
backgroundColor: 'var(--seekora-sort-bg)',
|
|
115
|
-
color: 'var(--seekora-sort-color)',
|
|
116
|
-
cursor: 'pointer',
|
|
117
|
-
outline: 'none',
|
|
109
|
+
React.createElement(CustomSelect, { value: value, onChange: applyValue, options: options, placeholder: placeholder, "aria-label": label || 'Sort results', className: clsx(sortByTheme.select), style: {
|
|
118
110
|
width: '100%',
|
|
119
|
-
|
|
111
|
+
'--seekora-select-font-size': fontSize,
|
|
112
|
+
'--seekora-select-border': 'var(--seekora-sort-border)',
|
|
113
|
+
'--seekora-select-radius': borderRadius,
|
|
114
|
+
'--seekora-select-bg': 'var(--seekora-sort-bg)',
|
|
115
|
+
'--seekora-select-color': 'var(--seekora-sort-color)',
|
|
116
|
+
} })));
|
|
120
117
|
}
|
|
121
118
|
// ------ Button group variant -------------------------------------------
|
|
122
119
|
if (variant === 'button-group') {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CustomSelect — lightweight, accessible custom dropdown replacement for native <select>.
|
|
3
|
+
*
|
|
4
|
+
* CSS Variables (apply on a parent element to customize):
|
|
5
|
+
* --seekora-select-bg — background color (default: #fff)
|
|
6
|
+
* --seekora-select-color — text color (default: inherit)
|
|
7
|
+
* --seekora-select-border — border color (default: rgba(128,128,128,0.3))
|
|
8
|
+
* --seekora-select-hover-bg — option hover background (default: #f3f4f6)
|
|
9
|
+
* --seekora-select-active-bg — selected option background (default: #eff6ff)
|
|
10
|
+
* --seekora-select-active-color — selected option text color (default: inherit)
|
|
11
|
+
* --seekora-select-radius — border radius (default: 6px)
|
|
12
|
+
* --seekora-select-font-size — font size (default: 0.875rem)
|
|
13
|
+
*/
|
|
14
|
+
import React from 'react';
|
|
15
|
+
export interface CustomSelectOption {
|
|
16
|
+
value: string;
|
|
17
|
+
label: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface CustomSelectTheme {
|
|
21
|
+
trigger?: string;
|
|
22
|
+
menu?: string;
|
|
23
|
+
option?: string;
|
|
24
|
+
optionActive?: string;
|
|
25
|
+
optionDisabled?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CustomSelectProps {
|
|
28
|
+
value: string;
|
|
29
|
+
onChange: (value: string) => void;
|
|
30
|
+
options: CustomSelectOption[];
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
className?: string;
|
|
33
|
+
style?: React.CSSProperties;
|
|
34
|
+
theme?: CustomSelectTheme;
|
|
35
|
+
/** aria-label for the trigger button */
|
|
36
|
+
'aria-label'?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare const CustomSelect: React.FC<CustomSelectProps>;
|
|
40
|
+
//# sourceMappingURL=CustomSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomSelect.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/CustomSelect.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAA0D,MAAM,OAAO,CAAC;AAO/E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqQpD,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CustomSelect — lightweight, accessible custom dropdown replacement for native <select>.
|
|
3
|
+
*
|
|
4
|
+
* CSS Variables (apply on a parent element to customize):
|
|
5
|
+
* --seekora-select-bg — background color (default: #fff)
|
|
6
|
+
* --seekora-select-color — text color (default: inherit)
|
|
7
|
+
* --seekora-select-border — border color (default: rgba(128,128,128,0.3))
|
|
8
|
+
* --seekora-select-hover-bg — option hover background (default: #f3f4f6)
|
|
9
|
+
* --seekora-select-active-bg — selected option background (default: #eff6ff)
|
|
10
|
+
* --seekora-select-active-color — selected option text color (default: inherit)
|
|
11
|
+
* --seekora-select-radius — border radius (default: 6px)
|
|
12
|
+
* --seekora-select-font-size — font size (default: 0.875rem)
|
|
13
|
+
*/
|
|
14
|
+
import React, { useState, useRef, useCallback, useEffect, useId } from 'react';
|
|
15
|
+
import { clsx } from 'clsx';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Component
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
export const CustomSelect = ({ value, onChange, options, placeholder = 'Select...', className, style, theme: customTheme, 'aria-label': ariaLabel, disabled = false, }) => {
|
|
20
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
21
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
22
|
+
const containerRef = useRef(null);
|
|
23
|
+
const menuRef = useRef(null);
|
|
24
|
+
const instanceId = useId();
|
|
25
|
+
const t = customTheme || {};
|
|
26
|
+
const selectedOption = options.find((o) => o.value === value);
|
|
27
|
+
const displayLabel = selectedOption?.label ?? placeholder;
|
|
28
|
+
// Close on click outside
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!isOpen)
|
|
31
|
+
return;
|
|
32
|
+
const handleMouseDown = (e) => {
|
|
33
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
34
|
+
setIsOpen(false);
|
|
35
|
+
setActiveIndex(-1);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
document.addEventListener('mousedown', handleMouseDown);
|
|
39
|
+
return () => document.removeEventListener('mousedown', handleMouseDown);
|
|
40
|
+
}, [isOpen]);
|
|
41
|
+
// Scroll active item into view
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!isOpen || activeIndex < 0 || !menuRef.current)
|
|
44
|
+
return;
|
|
45
|
+
const item = menuRef.current.children[activeIndex];
|
|
46
|
+
item?.scrollIntoView?.({ block: 'nearest' });
|
|
47
|
+
}, [activeIndex, isOpen]);
|
|
48
|
+
const toggle = useCallback(() => {
|
|
49
|
+
if (disabled)
|
|
50
|
+
return;
|
|
51
|
+
setIsOpen((prev) => {
|
|
52
|
+
if (!prev) {
|
|
53
|
+
// Opening — highlight current selection
|
|
54
|
+
const idx = options.findIndex((o) => o.value === value);
|
|
55
|
+
setActiveIndex(idx >= 0 ? idx : 0);
|
|
56
|
+
}
|
|
57
|
+
return !prev;
|
|
58
|
+
});
|
|
59
|
+
}, [disabled, options, value]);
|
|
60
|
+
const selectOption = useCallback((opt) => {
|
|
61
|
+
if (opt.disabled)
|
|
62
|
+
return;
|
|
63
|
+
onChange(opt.value);
|
|
64
|
+
setIsOpen(false);
|
|
65
|
+
setActiveIndex(-1);
|
|
66
|
+
}, [onChange]);
|
|
67
|
+
// Find next non-disabled index in given direction
|
|
68
|
+
const findNextEnabled = (from, dir) => {
|
|
69
|
+
let idx = from;
|
|
70
|
+
for (let i = 0; i < options.length; i++) {
|
|
71
|
+
idx += dir;
|
|
72
|
+
if (idx < 0)
|
|
73
|
+
idx = options.length - 1;
|
|
74
|
+
if (idx >= options.length)
|
|
75
|
+
idx = 0;
|
|
76
|
+
if (!options[idx].disabled)
|
|
77
|
+
return idx;
|
|
78
|
+
}
|
|
79
|
+
return from;
|
|
80
|
+
};
|
|
81
|
+
const handleKeyDown = useCallback((e) => {
|
|
82
|
+
if (disabled)
|
|
83
|
+
return;
|
|
84
|
+
if (!isOpen) {
|
|
85
|
+
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
toggle();
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
switch (e.key) {
|
|
92
|
+
case 'ArrowDown':
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
setActiveIndex((prev) => findNextEnabled(prev, 1));
|
|
95
|
+
break;
|
|
96
|
+
case 'ArrowUp':
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
setActiveIndex((prev) => findNextEnabled(prev, -1));
|
|
99
|
+
break;
|
|
100
|
+
case 'Enter':
|
|
101
|
+
case ' ':
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
if (activeIndex >= 0 && !options[activeIndex].disabled) {
|
|
104
|
+
selectOption(options[activeIndex]);
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
case 'Escape':
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
setIsOpen(false);
|
|
110
|
+
setActiveIndex(-1);
|
|
111
|
+
break;
|
|
112
|
+
case 'Home':
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
setActiveIndex(findNextEnabled(-1, 1));
|
|
115
|
+
break;
|
|
116
|
+
case 'End':
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
setActiveIndex(findNextEnabled(options.length, -1));
|
|
119
|
+
break;
|
|
120
|
+
case 'Tab':
|
|
121
|
+
setIsOpen(false);
|
|
122
|
+
setActiveIndex(-1);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}, [disabled, isOpen, activeIndex, options, toggle, selectOption]);
|
|
126
|
+
const menuId = `seekora-select-menu-${instanceId}`;
|
|
127
|
+
return (React.createElement("div", { ref: containerRef, className: clsx('seekora-select', className), style: { position: 'relative', display: 'inline-block', ...style }, onKeyDown: handleKeyDown },
|
|
128
|
+
React.createElement("button", { type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": menuId, "aria-label": ariaLabel, "aria-activedescendant": isOpen && activeIndex >= 0
|
|
129
|
+
? `${menuId}-opt-${activeIndex}`
|
|
130
|
+
: undefined, disabled: disabled, onClick: toggle, className: clsx('seekora-select__trigger', t.trigger), style: {
|
|
131
|
+
display: 'flex',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
justifyContent: 'space-between',
|
|
134
|
+
gap: 8,
|
|
135
|
+
width: '100%',
|
|
136
|
+
padding: '8px 12px',
|
|
137
|
+
fontSize: 'var(--seekora-select-font-size, 0.875rem)',
|
|
138
|
+
lineHeight: 1.4,
|
|
139
|
+
border: '1px solid var(--seekora-select-border, rgba(128,128,128,0.3))',
|
|
140
|
+
borderRadius: 'var(--seekora-select-radius, 6px)',
|
|
141
|
+
backgroundColor: 'var(--seekora-select-bg, #fff)',
|
|
142
|
+
color: 'var(--seekora-select-color, inherit)',
|
|
143
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
144
|
+
outline: 'none',
|
|
145
|
+
textAlign: 'left',
|
|
146
|
+
fontFamily: 'inherit',
|
|
147
|
+
opacity: disabled ? 0.5 : 1,
|
|
148
|
+
} },
|
|
149
|
+
React.createElement("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, displayLabel),
|
|
150
|
+
React.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: {
|
|
151
|
+
flexShrink: 0,
|
|
152
|
+
transition: 'transform 0.15s ease',
|
|
153
|
+
transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
154
|
+
} },
|
|
155
|
+
React.createElement("path", { d: "M2.5 4.5L6 8L9.5 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }))),
|
|
156
|
+
isOpen && (React.createElement("div", { ref: menuRef, id: menuId, role: "listbox", className: clsx('seekora-select__menu', t.menu), style: {
|
|
157
|
+
position: 'absolute',
|
|
158
|
+
top: '100%',
|
|
159
|
+
left: 0,
|
|
160
|
+
right: 0,
|
|
161
|
+
zIndex: 9999,
|
|
162
|
+
marginTop: 4,
|
|
163
|
+
padding: '4px 0',
|
|
164
|
+
border: '1px solid var(--seekora-select-border, rgba(128,128,128,0.3))',
|
|
165
|
+
borderRadius: 'var(--seekora-select-radius, 6px)',
|
|
166
|
+
backgroundColor: 'var(--seekora-select-bg, #fff)',
|
|
167
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
|
168
|
+
maxHeight: 220,
|
|
169
|
+
overflowY: 'auto',
|
|
170
|
+
} }, options.map((opt, idx) => {
|
|
171
|
+
const isSelected = opt.value === value;
|
|
172
|
+
const isHighlighted = idx === activeIndex;
|
|
173
|
+
return (React.createElement("div", { key: opt.value, id: `${menuId}-opt-${idx}`, role: "option", "aria-selected": isSelected, "aria-disabled": opt.disabled || undefined, className: clsx('seekora-select__option', t.option, isSelected && 'seekora-select__option--selected', isHighlighted && (t.optionActive || 'seekora-select__option--highlighted'), opt.disabled && (t.optionDisabled || 'seekora-select__option--disabled')), onMouseEnter: () => !opt.disabled && setActiveIndex(idx), onMouseDown: (e) => {
|
|
174
|
+
e.preventDefault(); // Prevent blur
|
|
175
|
+
if (!opt.disabled)
|
|
176
|
+
selectOption(opt);
|
|
177
|
+
}, style: {
|
|
178
|
+
padding: '6px 12px',
|
|
179
|
+
fontSize: 'var(--seekora-select-font-size, 0.875rem)',
|
|
180
|
+
cursor: opt.disabled ? 'not-allowed' : 'pointer',
|
|
181
|
+
backgroundColor: isHighlighted
|
|
182
|
+
? 'var(--seekora-select-hover-bg, #f3f4f6)'
|
|
183
|
+
: isSelected
|
|
184
|
+
? 'var(--seekora-select-active-bg, #eff6ff)'
|
|
185
|
+
: 'transparent',
|
|
186
|
+
color: opt.disabled
|
|
187
|
+
? 'rgba(128,128,128,0.5)'
|
|
188
|
+
: isSelected
|
|
189
|
+
? 'var(--seekora-select-active-color, inherit)'
|
|
190
|
+
: 'var(--seekora-select-color, inherit)',
|
|
191
|
+
fontWeight: isSelected ? 500 : 400,
|
|
192
|
+
opacity: opt.disabled ? 0.5 : 1,
|
|
193
|
+
transition: 'background-color 0.1s ease',
|
|
194
|
+
} }, opt.label));
|
|
195
|
+
})))));
|
|
196
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageZoom.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/ImageZoom.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ImageZoom.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/ImageZoom.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,+BAA+B;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,SAAS,CAAC,EACxB,GAAG,EACH,GAAQ,EACR,IAAa,EACb,SAAe,EACf,SAAS,EACT,KAAK,EACL,iBAAwB,EACxB,QAAc,EACd,aAA2C,EAC3C,MAAM,EACN,YAAgB,GACjB,EAAE,cAAc,qBAwjBhB"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - click: Full-screen lightbox modal
|
|
8
8
|
*/
|
|
9
9
|
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
10
|
+
import { createPortal } from 'react-dom';
|
|
10
11
|
import { clsx } from 'clsx';
|
|
11
12
|
export function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, className, style, showZoomIndicator = true, lensSize = 150, zoomPanelSize = { width: 400, height: 400 }, images, currentIndex = 0, }) {
|
|
12
13
|
const [isHovering, setIsHovering] = useState(false);
|
|
@@ -107,13 +108,30 @@ export function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, class
|
|
|
107
108
|
objectFit: 'cover',
|
|
108
109
|
display: 'block',
|
|
109
110
|
};
|
|
111
|
+
// Compute the indicator rectangle (what portion of the image the zoom panel shows).
|
|
112
|
+
// indW / containerW = 1 / zoomLevel (the panel shows 1/zoomLevel of the image width).
|
|
113
|
+
// indH is derived from the zoom panel's aspect ratio so it's consistent with what the
|
|
114
|
+
// panel actually renders — no need for the image's natural dimensions.
|
|
115
|
+
const getIndicatorRect = useCallback(() => {
|
|
116
|
+
if (!containerRef.current)
|
|
117
|
+
return { indL: 0, indT: 0, indW: 0, indH: 0 };
|
|
118
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
119
|
+
const indW = rect.width / zoomLevel;
|
|
120
|
+
const indH = (zoomPanelSize.height / zoomPanelSize.width) * rect.width / zoomLevel;
|
|
121
|
+
const indL = Math.max(0, Math.min(rect.width - indW, cursorPos.x - indW / 2));
|
|
122
|
+
const indT = Math.max(0, Math.min(rect.height - indH, cursorPos.y - indH / 2));
|
|
123
|
+
return { indL, indT, indW, indH };
|
|
124
|
+
}, [cursorPos, zoomLevel, zoomPanelSize]);
|
|
110
125
|
// Calculate zoom panel background position (Amazon-style)
|
|
111
126
|
const getZoomPanelStyle = () => {
|
|
112
127
|
if (!containerRef.current || !imageLoaded)
|
|
113
128
|
return { display: 'none' };
|
|
114
129
|
const rect = containerRef.current.getBoundingClientRect();
|
|
115
|
-
const
|
|
116
|
-
|
|
130
|
+
const { indL, indT, indW, indH } = getIndicatorRect();
|
|
131
|
+
// bgPos is derived from the clamped indicator position, not the raw cursor.
|
|
132
|
+
// At indL=0 → bgPosX=0% (show left), at indL=containerW-indW → bgPosX=100% (show right).
|
|
133
|
+
const bgPosX = (rect.width - indW) > 0 ? (indL / (rect.width - indW)) * 100 : 0;
|
|
134
|
+
const bgPosY = (rect.height - indH) > 0 ? (indT / (rect.height - indH)) * 100 : 0;
|
|
117
135
|
// Calculate available space in all directions
|
|
118
136
|
const viewportWidth = window.innerWidth;
|
|
119
137
|
const viewportHeight = window.innerHeight;
|
|
@@ -241,19 +259,22 @@ export function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, class
|
|
|
241
259
|
} }, "\uD83D\uDD0D")),
|
|
242
260
|
supportsLens && isHovering && imageLoaded && (React.createElement("div", { style: getLensStyle() },
|
|
243
261
|
React.createElement("img", { src: src, alt: "", style: getLensImageStyle() }))),
|
|
244
|
-
supportsHover && isHovering && imageLoaded && containerRef.current && (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
262
|
+
supportsHover && isHovering && imageLoaded && containerRef.current && (() => {
|
|
263
|
+
const { indL, indT, indW, indH } = getIndicatorRect();
|
|
264
|
+
return (React.createElement("div", { style: {
|
|
265
|
+
position: 'absolute',
|
|
266
|
+
left: indL,
|
|
267
|
+
top: indT,
|
|
268
|
+
width: indW,
|
|
269
|
+
height: indH,
|
|
270
|
+
border: 'var(--seekora-hover-area-border, 2px solid rgba(0,0,0,0.3))',
|
|
271
|
+
backgroundColor: 'var(--seekora-hover-area-bg, rgba(255,255,255,0.1))',
|
|
272
|
+
pointerEvents: 'none',
|
|
273
|
+
zIndex: 50,
|
|
274
|
+
} }));
|
|
275
|
+
})()),
|
|
276
|
+
supportsHover && isHovering && imageLoaded && typeof document !== 'undefined' && createPortal(React.createElement("div", { style: getZoomPanelStyle() }), document.body),
|
|
277
|
+
isLightboxOpen && typeof document !== 'undefined' && createPortal(React.createElement("div", { className: "seekora-image-zoom-lightbox", style: {
|
|
257
278
|
position: 'fixed',
|
|
258
279
|
top: 0,
|
|
259
280
|
left: 0,
|
|
@@ -408,5 +429,5 @@ export function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, class
|
|
|
408
429
|
backgroundColor: 'var(--seekora-lightbox-instructions-bg, rgba(0,0,0,0.5))',
|
|
409
430
|
padding: '8px 16px',
|
|
410
431
|
borderRadius: 12,
|
|
411
|
-
} }, hasMultipleImages ? 'Use arrow keys or click thumbnails to navigate • ESC to close' : 'Click outside or press ESC to close')))));
|
|
432
|
+
} }, hasMultipleImages ? 'Use arrow keys or click thumbnails to navigate • ESC to close' : 'Click outside or press ESC to close')), document.body)));
|
|
412
433
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VariantSelector.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/VariantSelector.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"VariantSelector.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/VariantSelector.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG9E,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,iBAAiB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC;IACrE,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,8CAA8C;IAC9C,eAAe,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAqDD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAqB,EACrB,QAAQ,EACR,gBAAuB,EACvB,eAAe,EAAE,gBAAgB,EACjC,SAAS,EACT,KAAK,GACN,EAAE,oBAAoB,4BAsKtB"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
|
+
import { CustomSelect } from './CustomSelect';
|
|
11
12
|
const COLOR_NAMES = {
|
|
12
13
|
black: '#000', white: '#fff', red: '#ef4444', blue: '#3b82f6',
|
|
13
14
|
green: '#22c55e', yellow: '#eab308', orange: '#f97316', purple: '#a855f7',
|
|
@@ -80,30 +81,22 @@ export function VariantSelector({ options, variants, selections, onSelectionChan
|
|
|
80
81
|
} },
|
|
81
82
|
option.name,
|
|
82
83
|
selected && (React.createElement("span", { style: { fontWeight: 400, marginLeft: 6, color: 'var(--seekora-text-secondary, inherit)' } }, selected))),
|
|
83
|
-
mode === 'dropdown' ? (React.createElement("
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
option.
|
|
99
|
-
option.values.map((value) => {
|
|
100
|
-
const available = showAvailability
|
|
101
|
-
? getAvailability(option.name, value, options, variants, selections)
|
|
102
|
-
: true;
|
|
103
|
-
return (React.createElement("option", { key: value, value: value, disabled: !available },
|
|
104
|
-
value,
|
|
105
|
-
!available ? ' (Unavailable)' : ''));
|
|
106
|
-
}))) : (React.createElement("div", { className: "seekora-variant-buttons", style: { display: 'flex', flexWrap: 'wrap', gap: 8 } }, option.values.map((value) => {
|
|
84
|
+
mode === 'dropdown' ? (React.createElement("div", { onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation() },
|
|
85
|
+
React.createElement(CustomSelect, { className: "seekora-variant-dropdown", value: selected ?? '', onChange: (val) => onSelectionChange(option.name, val), placeholder: `Select ${option.name}`, "aria-label": `Select ${option.name}`, options: option.values.map((val) => {
|
|
86
|
+
const available = showAvailability
|
|
87
|
+
? getAvailability(option.name, val, options, variants, selections)
|
|
88
|
+
: true;
|
|
89
|
+
return {
|
|
90
|
+
value: val,
|
|
91
|
+
label: available ? val : `${val} (Unavailable)`,
|
|
92
|
+
disabled: !available,
|
|
93
|
+
};
|
|
94
|
+
}), style: {
|
|
95
|
+
minWidth: 120,
|
|
96
|
+
'--seekora-select-border': 'var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
97
|
+
'--seekora-select-bg': 'var(--seekora-bg-surface, transparent)',
|
|
98
|
+
'--seekora-select-color': 'var(--seekora-text-primary, inherit)',
|
|
99
|
+
} }))) : (React.createElement("div", { className: "seekora-variant-buttons", style: { display: 'flex', flexWrap: 'wrap', gap: 8 } }, option.values.map((value) => {
|
|
107
100
|
const isActive = selected === value;
|
|
108
101
|
const available = showAvailability
|
|
109
102
|
? getAvailability(option.name, value, options, variants, selections)
|
|
@@ -8,4 +8,5 @@ export { VariantSelector, type VariantSelectorProps } from './VariantSelector';
|
|
|
8
8
|
export { ActionButtons, type ActionButtonsProps, type ActionButton, type ActionButtonType } from './ActionButtons';
|
|
9
9
|
export { withAnalytics, type WithAnalyticsConfig, type WithAnalyticsInjectedProps } from './withAnalytics';
|
|
10
10
|
export { AnalyticsProvider, useAnalyticsProvider, type AnalyticsProviderProps } from './AnalyticsProvider';
|
|
11
|
+
export { CustomSelect, type CustomSelectProps, type CustomSelectOption, type CustomSelectTheme } from './CustomSelect';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|