@kaizen/components 1.80.2 → 1.80.3

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 (68) hide show
  1. package/codemods/README.md +12 -0
  2. package/codemods/renameV2ComponentImportsAndUsages/index.ts +19 -0
  3. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.spec.ts +390 -0
  4. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.ts +230 -0
  5. package/codemods/utils/index.ts +1 -0
  6. package/codemods/utils/updateJsxElementTagName.spec.ts +129 -0
  7. package/codemods/utils/updateJsxElementTagName.ts +56 -0
  8. package/codemods/utils/updateKaioImports.spec.ts +82 -0
  9. package/codemods/utils/updateKaioImports.ts +16 -7
  10. package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +69 -16
  11. package/dist/cjs/src/__alpha__/SingleSelect/context/SingleSelectContext.cjs +13 -0
  12. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +54 -0
  13. package/dist/cjs/src/__alpha__/SingleSelect/{SingleSelect.module.css.cjs → subcomponents/Popover/Popover.module.css.cjs} +1 -1
  14. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +94 -0
  15. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +69 -0
  16. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.cjs +12 -0
  17. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +41 -5
  18. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +60 -10
  19. package/dist/esm/src/__alpha__/SingleSelect/context/SingleSelectContext.mjs +10 -0
  20. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +49 -0
  21. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +4 -0
  22. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +92 -0
  23. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +67 -0
  24. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.mjs +10 -0
  25. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +43 -7
  26. package/dist/styles.css +43 -21
  27. package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +7 -9
  28. package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +12 -0
  29. package/dist/types/__alpha__/SingleSelect/context/index.d.ts +1 -0
  30. package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -1
  31. package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -1
  32. package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -1
  33. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +6 -0
  34. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/index.d.ts +1 -0
  35. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +2 -0
  36. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +4 -0
  37. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +4 -0
  38. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.d.ts +1 -0
  39. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +2 -1
  40. package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +1 -0
  41. package/dist/types/__alpha__/SingleSelect/types.d.ts +45 -0
  42. package/package.json +4 -4
  43. package/src/__alpha__/SingleSelect/SingleSelect.tsx +79 -14
  44. package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +5 -2
  45. package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +100 -0
  46. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +4 -4
  47. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +21 -2
  48. package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +21 -0
  49. package/src/__alpha__/SingleSelect/context/index.ts +1 -0
  50. package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +0 -1
  51. package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +2 -1
  52. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +7 -0
  53. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +2 -1
  54. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +3 -1
  55. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +24 -0
  56. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +54 -0
  57. package/src/__alpha__/SingleSelect/subcomponents/Popover/index.ts +1 -0
  58. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +2 -0
  59. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +108 -0
  60. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +75 -0
  61. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts +13 -0
  62. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +1 -0
  63. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +29 -7
  64. package/src/__alpha__/SingleSelect/subcomponents/index.ts +1 -0
  65. package/src/__alpha__/SingleSelect/types.ts +58 -0
  66. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.module.css.mjs +0 -4
  67. package/src/__alpha__/SingleSelect/SingleSelect.module.css +0 -9
  68. package/src/__alpha__/SingleSelect/SingleSelect.spec.tsx +0 -26
@@ -0,0 +1,129 @@
1
+ import ts from 'typescript'
2
+ import { parseJsx } from '../__tests__/utils'
3
+ import { updateJsxElementTagName, type ComponentRenameConfig } from './updateJsxElementTagName'
4
+
5
+ const componentRenameMap = new Map<string, ComponentRenameConfig>([
6
+ [
7
+ 'Pancakes',
8
+ {
9
+ newName: 'Beer',
10
+ fromModule: '@kaizen/components/next',
11
+ toModule: '@kaizen/components',
12
+ },
13
+ ],
14
+ ['Bart', { newName: 'Bender', fromModule: '@kaizen/components', toModule: '@kaizen/components' }],
15
+ [
16
+ 'EatMyShorts',
17
+ { newName: 'MeatBags', fromModule: '@kaizen/components', toModule: '@kaizen/components' },
18
+ ],
19
+ ])
20
+
21
+ describe('updateJsxElementTagName()', () => {
22
+ const factory = ts.factory
23
+
24
+ it('should ignore nodes not in componentMap', () => {
25
+ const elem = factory.createJsxSelfClosingElement(
26
+ factory.createIdentifier('Cowabunga'),
27
+ undefined,
28
+ factory.createJsxAttributes([]),
29
+ )
30
+
31
+ const result = updateJsxElementTagName(factory, elem, 'Cowabunga', componentRenameMap)
32
+
33
+ expect(ts.isJsxSelfClosingElement(result)).toBe(true)
34
+ expect((result.tagName as ts.Identifier).text).toBe('Cowabunga')
35
+ })
36
+
37
+ it('should update self closing tag', () => {
38
+ const elem = factory.createJsxSelfClosingElement(
39
+ factory.createIdentifier('Pancakes'),
40
+ undefined,
41
+ factory.createJsxAttributes([]),
42
+ )
43
+
44
+ const result = updateJsxElementTagName(factory, elem, 'Beer', componentRenameMap)
45
+
46
+ expect(ts.isJsxSelfClosingElement(result)).toBe(true)
47
+ expect((result.tagName as ts.Identifier).text).toBe('Beer')
48
+ })
49
+
50
+ it('should handle Component.SubComponent when in Component is found in componentMap', () => {
51
+ const source = `<Bart.Shorts />`
52
+ const sourceFile = parseJsx(source)
53
+ const statement = sourceFile.statements[0] as ts.ExpressionStatement
54
+ const elem = statement.expression as ts.JsxSelfClosingElement
55
+
56
+ const result = updateJsxElementTagName(factory, elem, 'ignored', componentRenameMap)
57
+
58
+ expect(ts.isJsxSelfClosingElement(result)).toBe(true)
59
+ expect(ts.isPropertyAccessExpression(result.tagName)).toBe(true)
60
+
61
+ const tagName = result.tagName as ts.PropertyAccessExpression
62
+ expect((tagName.expression as ts.Identifier).text).toBe('Bender')
63
+ expect(tagName.name.text).toBe('Shorts')
64
+ })
65
+
66
+ it('should ignore Component.SubComponent when in Component is not in componentMap', () => {
67
+ const source = `<Not.Here />`
68
+ const sourceFile = parseJsx(source)
69
+ const statement = sourceFile.statements[0] as ts.ExpressionStatement
70
+ const elem = statement.expression as ts.JsxSelfClosingElement
71
+
72
+ const result = updateJsxElementTagName(factory, elem, 'ignored', componentRenameMap)
73
+
74
+ expect(ts.isJsxSelfClosingElement(result)).toBe(true)
75
+ expect(ts.isPropertyAccessExpression(result.tagName)).toBe(true)
76
+
77
+ const tagName = result.tagName as ts.PropertyAccessExpression
78
+ expect((tagName.expression as ts.Identifier).text).toBe('Not')
79
+ expect(tagName.name.text).toBe('Here')
80
+ })
81
+
82
+ it('should update JSX opening element tag name', () => {
83
+ const openingElement = factory.createJsxOpeningElement(
84
+ factory.createIdentifier('Pancakes'),
85
+ undefined,
86
+ factory.createJsxAttributes([]),
87
+ )
88
+
89
+ const result = updateJsxElementTagName(factory, openingElement, 'Pizza', componentRenameMap)
90
+
91
+ expect(ts.isJsxOpeningElement(result)).toBe(true)
92
+ expect((result.tagName as ts.Identifier).text).toBe('Pizza')
93
+ })
94
+
95
+ it('should update JSX closing element tag name', () => {
96
+ const closingElement = factory.createJsxClosingElement(factory.createIdentifier('EatMyShorts'))
97
+
98
+ const result = updateJsxElementTagName(factory, closingElement, 'MeatBags', componentRenameMap)
99
+
100
+ expect(ts.isJsxClosingElement(result)).toBe(true)
101
+ expect((result.tagName as ts.Identifier).text).toBe('MeatBags')
102
+ })
103
+
104
+ it('should preserve attributes when updating JSX elements', () => {
105
+ const attributes = factory.createJsxAttributes([
106
+ factory.createJsxAttribute(
107
+ factory.createIdentifier('className'),
108
+ factory.createStringLiteral('test-class'),
109
+ ),
110
+ ])
111
+
112
+ const element = factory.createJsxSelfClosingElement(
113
+ factory.createIdentifier('Pancakes'),
114
+ undefined,
115
+ attributes,
116
+ )
117
+
118
+ const result = updateJsxElementTagName(factory, element, 'Pizza', componentRenameMap)
119
+
120
+ expect(ts.isJsxSelfClosingElement(result)).toBe(true)
121
+ expect((result.tagName as ts.Identifier).text).toBe('Pizza')
122
+ expect((result as ts.JsxSelfClosingElement).attributes.properties.length).toBe(1)
123
+
124
+ const attribute = (result as ts.JsxSelfClosingElement).attributes
125
+ .properties[0] as ts.JsxAttribute
126
+ expect((attribute.name as ts.Identifier).text).toBe('className')
127
+ expect((attribute.initializer as ts.StringLiteral).text).toBe('test-class')
128
+ })
129
+ })
@@ -0,0 +1,56 @@
1
+ import ts from 'typescript'
2
+
3
+ export type ComponentRenameConfig = {
4
+ newName: string
5
+ fromModule: string
6
+ toModule: string
7
+ }
8
+
9
+ export const updateJsxElementTagName = (
10
+ factory: ts.NodeFactory,
11
+ node: ts.JsxOpeningElement | ts.JsxClosingElement | ts.JsxSelfClosingElement,
12
+ newTagName: string,
13
+ componentRenameMap: Map<string, ComponentRenameConfig>,
14
+ ): ts.JsxOpeningElement | ts.JsxClosingElement | ts.JsxSelfClosingElement => {
15
+ let newTagNameExpr: ts.JsxTagNameExpression
16
+
17
+ if (ts.isPropertyAccessExpression(node.tagName)) {
18
+ const baseComponentName = node.tagName.expression.getText()
19
+ const rename = componentRenameMap.get(baseComponentName)
20
+
21
+ if (rename) {
22
+ newTagNameExpr = factory.createPropertyAccessExpression(
23
+ factory.createIdentifier(rename.newName),
24
+ node.tagName.name,
25
+ ) as ts.JsxTagNameExpression
26
+ } else {
27
+ newTagNameExpr = node.tagName
28
+ }
29
+ } else {
30
+ newTagNameExpr = factory.createIdentifier(newTagName)
31
+ }
32
+
33
+ if (ts.isJsxSelfClosingElement(node)) {
34
+ return factory.updateJsxSelfClosingElement(
35
+ node,
36
+ newTagNameExpr,
37
+ node.typeArguments,
38
+ node.attributes,
39
+ )
40
+ }
41
+
42
+ if (ts.isJsxOpeningElement(node)) {
43
+ return factory.updateJsxOpeningElement(
44
+ node,
45
+ newTagNameExpr,
46
+ node.typeArguments,
47
+ node.attributes,
48
+ )
49
+ }
50
+
51
+ if (ts.isJsxClosingElement(node)) {
52
+ return factory.updateJsxClosingElement(node, newTagNameExpr)
53
+ }
54
+
55
+ return node
56
+ }
@@ -200,6 +200,88 @@ describe('updateKaioImports()', () => {
200
200
  }),
201
201
  ).toEqual(printAst(outputAst))
202
202
  })
203
+
204
+ describe('type-only imports', () => {
205
+ it('creates a new type-only import declaration', () => {
206
+ const inputAst = parseJsx(`
207
+ import { Well } from "@kaizen/components"
208
+ `)
209
+ const outputAst = parseJsx(`
210
+ import { Well } from "@kaizen/components"
211
+ import type { Card } from "@kaizen/components/next"
212
+ `)
213
+ expect(
214
+ transformInput(inputAst)({
215
+ importsToAdd: new Map([
216
+ [
217
+ '@kaizen/components/next',
218
+ new Map([['Card', { componentName: 'Card', isTypeOnly: true }]]),
219
+ ],
220
+ ]),
221
+ }),
222
+ ).toEqual(printAst(outputAst))
223
+ })
224
+
225
+ it('adds type-only import to existing regular imports', () => {
226
+ const inputAst = parseJsx(`
227
+ import { Select } from "@kaizen/components/next"
228
+ `)
229
+ const outputAst = parseJsx(`
230
+ import { Select, type Card } from "@kaizen/components/next"
231
+ `)
232
+ expect(
233
+ transformInput(inputAst)({
234
+ importsToAdd: new Map([
235
+ [
236
+ '@kaizen/components/next',
237
+ new Map([['Card', { componentName: 'Card', isTypeOnly: true }]]),
238
+ ],
239
+ ]),
240
+ }),
241
+ ).toEqual(printAst(outputAst))
242
+ })
243
+
244
+ it('adds type-only import to existing type-only imports', () => {
245
+ const inputAst = parseJsx(`
246
+ import type { CardProps } from "@kaizen/components/next"
247
+ `)
248
+ const outputAst = parseJsx(`
249
+ import type { CardProps, type ButtonProps } from "@kaizen/components/next"
250
+ `)
251
+ expect(
252
+ transformInput(inputAst)({
253
+ importsToAdd: new Map([
254
+ [
255
+ '@kaizen/components/next',
256
+ new Map([['ButtonProps', { componentName: 'ButtonProps', isTypeOnly: true }]]),
257
+ ],
258
+ ]),
259
+ }),
260
+ ).toEqual(printAst(outputAst))
261
+ })
262
+
263
+ it('adds mix of type-only and regular imports', () => {
264
+ const inputAst = parseJsx(`
265
+ import { Select } from "@kaizen/components/next"
266
+ `)
267
+ const outputAst = parseJsx(`
268
+ import { Select, type CardProps, Button } from "@kaizen/components/next"
269
+ `)
270
+ expect(
271
+ transformInput(inputAst)({
272
+ importsToAdd: new Map([
273
+ [
274
+ '@kaizen/components/next',
275
+ new Map([
276
+ ['CardProps', { componentName: 'CardProps', isTypeOnly: true }],
277
+ ['Button', { componentName: 'Button', isTypeOnly: false }],
278
+ ]),
279
+ ],
280
+ ]),
281
+ }),
282
+ ).toEqual(printAst(outputAst))
283
+ })
284
+ })
203
285
  })
204
286
  })
205
287
  })
@@ -34,7 +34,7 @@ const removeNamedImports = (
34
34
  return node
35
35
  }
36
36
 
37
- type NewImportAttributes = { componentName: string; alias?: string }
37
+ type NewImportAttributes = { componentName: string; alias?: string; isTypeOnly?: boolean }
38
38
  type ImportsToAdd = Map<string, NewImportAttributes>
39
39
 
40
40
  const createImportDeclaration = (
@@ -42,9 +42,12 @@ const createImportDeclaration = (
42
42
  importsToAdd: ImportsToAdd,
43
43
  moduleSpecifier: string,
44
44
  ): ts.ImportDeclaration => {
45
- const namedImports = Array.from(importsToAdd.values()).map(({ componentName, alias }) =>
45
+ const imports = Array.from(importsToAdd.values())
46
+ const allTypeOnly = imports.every(({ isTypeOnly }) => isTypeOnly)
47
+
48
+ const namedImports = imports.map(({ componentName, alias, isTypeOnly }) =>
46
49
  factory.createImportSpecifier(
47
- false,
50
+ allTypeOnly ? false : (isTypeOnly ?? false),
48
51
  alias ? factory.createIdentifier(componentName) : undefined,
49
52
  factory.createIdentifier(alias ?? componentName),
50
53
  ),
@@ -52,7 +55,7 @@ const createImportDeclaration = (
52
55
 
53
56
  return factory.createImportDeclaration(
54
57
  undefined,
55
- factory.createImportClause(false, undefined, factory.createNamedImports(namedImports)),
58
+ factory.createImportClause(allTypeOnly, undefined, factory.createNamedImports(namedImports)),
56
59
  factory.createStringLiteral(moduleSpecifier),
57
60
  )
58
61
  }
@@ -75,9 +78,13 @@ const updateNamedImports = (
75
78
  })
76
79
  }
77
80
 
78
- Array.from(importsToAdd.values()).forEach(({ alias, componentName }) => {
81
+ const newImports = Array.from(importsToAdd.values())
82
+ const allNewTypeOnly = newImports.every(({ isTypeOnly }) => isTypeOnly)
83
+ const hasExistingImports = importSpecifiers.length > 0
84
+
85
+ newImports.forEach(({ alias, componentName, isTypeOnly }) => {
79
86
  const newImport = factory.createImportSpecifier(
80
- false,
87
+ !allNewTypeOnly || hasExistingImports ? (isTypeOnly ?? false) : false,
81
88
  alias ? factory.createIdentifier(componentName) : undefined,
82
89
  factory.createIdentifier(alias ?? componentName),
83
90
  )
@@ -87,12 +94,14 @@ const updateNamedImports = (
87
94
  }
88
95
  })
89
96
 
97
+ const isModuleLevelTypeOnly = allNewTypeOnly && !hasExistingImports
98
+
90
99
  return factory.updateImportDeclaration(
91
100
  node,
92
101
  node.modifiers,
93
102
  factory.updateImportClause(
94
103
  node.importClause,
95
- node.importClause.isTypeOnly,
104
+ isModuleLevelTypeOnly || node.importClause.isTypeOnly,
96
105
  node.importClause.name,
97
106
  factory.createNamedImports(importSpecifiers),
98
107
  ),
@@ -2,31 +2,84 @@
2
2
 
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
+ var select = require('@react-stately/select');
5
6
  var reactAriaComponents = require('react-aria-components');
7
+ var SingleSelectContext = require('./context/SingleSelectContext.cjs');
6
8
  var List = require('./subcomponents/List/List.cjs');
7
9
  var ListSection = require('./subcomponents/ListSection/ListSection.cjs');
8
10
  var ListItem = require('./subcomponents/ListItem/ListItem.cjs');
9
11
  var Trigger = require('./subcomponents/Trigger/Trigger.cjs');
10
- var SingleSelect_module = require('./SingleSelect.module.css.cjs');
12
+ var Popover = require('./subcomponents/Popover/Popover.cjs');
11
13
  function _interopDefault(e) {
12
14
  return e && e.__esModule ? e : {
13
15
  default: e
14
16
  };
15
17
  }
16
18
  var React__default = /*#__PURE__*/_interopDefault(React);
17
- var SingleSelect = function (_a) {
18
- var classNameOverride = _a.classNameOverride,
19
- children = _a.children,
20
- restProps = tslib.__rest(_a, ["classNameOverride", "children"]);
21
- return React__default.default.createElement(reactAriaComponents.Select, tslib.__assign({
22
- className: classNameOverride,
23
- placeholder: ""
24
- }, restProps), React__default.default.createElement(Trigger.Trigger, null), React__default.default.createElement(reactAriaComponents.Popover, {
25
- className: SingleSelect_module.popover
26
- }, children));
27
- };
28
- SingleSelect.displayName = 'SingleSelect';
29
- SingleSelect.List = List.List;
30
- SingleSelect.ListItem = ListItem.ListItem;
31
- SingleSelect.ListSection = ListSection.ListSection;
19
+ const SingleSelect = /*#__PURE__*/function () {
20
+ const SingleSelect = function (_a) {
21
+ var items = _a.items,
22
+ onSelectionChange = _a.onSelectionChange,
23
+ children = _a.children,
24
+ restProps = tslib.__rest(_a, ["items", "onSelectionChange", "children"]);
25
+ var buttonRef = React__default.default.useRef(null);
26
+ var popoverRef = React__default.default.useRef(null);
27
+ var racPopoverRef = React__default.default.useRef(null);
28
+ var uniqueId = React.useId();
29
+ var anchorName = "--trigger-".concat(uniqueId);
30
+ var state = select.useSelectState({
31
+ items: items
32
+ });
33
+ var handleOnSelectionChange = React__default.default.useCallback(function (keys) {
34
+ var key = null;
35
+ if (keys instanceof Set && keys.size > 0) {
36
+ key = Array.from(keys)[0];
37
+ }
38
+ state.setSelectedKey(key);
39
+ if (onSelectionChange) {
40
+ onSelectionChange(key);
41
+ }
42
+ }, [state, onSelectionChange]);
43
+ // Cloning children here to allow users to pass in a custom ListItem or ListSection
44
+ // and still have the SingleSelect handle selection state
45
+ var injectedChildren = React.useMemo(function () {
46
+ if (!React.isValidElement(children)) return null;
47
+ var selectedKeys = state.selectedKey ? new Set([state.selectedKey]) : new Set();
48
+ return React.cloneElement(children, {
49
+ selectionMode: 'single',
50
+ selectedKeys: selectedKeys,
51
+ onSelectionChange: handleOnSelectionChange,
52
+ autoFocus: 'first'
53
+ });
54
+ }, [children, handleOnSelectionChange, state.selectedKey]);
55
+ return React__default.default.createElement(SingleSelectContext.SingleSelectContext.Provider, {
56
+ value: {
57
+ isOpen: state.isOpen,
58
+ setOpen: state.setOpen,
59
+ selectedKey: state.selectedKey,
60
+ items: items,
61
+ anchorName: anchorName
62
+ }
63
+ }, React__default.default.createElement(reactAriaComponents.Select
64
+ // TODO: allow user to pass in label
65
+ , tslib.__assign({
66
+ "aria-label": 'single-select',
67
+ onSelectionChange: function (key) {
68
+ return handleOnSelectionChange(key != null ? new Set([key]) : new Set());
69
+ },
70
+ placeholder: ""
71
+ }, restProps), React__default.default.createElement(Trigger.Trigger, {
72
+ buttonRef: buttonRef
73
+ }), state.isOpen && React__default.default.createElement(Popover.Popover, {
74
+ buttonRef: buttonRef,
75
+ popoverRef: popoverRef,
76
+ racPopoverRef: racPopoverRef
77
+ }, injectedChildren)));
78
+ };
79
+ SingleSelect.displayName = 'SingleSelect';
80
+ SingleSelect.List = List.List;
81
+ SingleSelect.ListItem = ListItem.ListItem;
82
+ SingleSelect.ListSection = ListSection.ListSection;
83
+ return SingleSelect;
84
+ }();
32
85
  exports.SingleSelect = SingleSelect;
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var SingleSelectContext = React.createContext(undefined);
5
+ var useSingleSelectContext = function () {
6
+ var context = React.useContext(SingleSelectContext);
7
+ if (!context) {
8
+ throw new Error('useSingleSelectContext must be used within a SingleSelectContext.Provider');
9
+ }
10
+ return context;
11
+ };
12
+ exports.SingleSelectContext = SingleSelectContext;
13
+ exports.useSingleSelectContext = useSingleSelectContext;
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var reactAriaComponents = require('react-aria-components');
5
+ var SingleSelectContext = require('../../context/SingleSelectContext.cjs');
6
+ var usePositioningStyles = require('./utils/usePositioningStyles.cjs');
7
+ var Popover_module = require('./Popover.module.css.cjs');
8
+ function _interopDefault(e) {
9
+ return e && e.__esModule ? e : {
10
+ default: e
11
+ };
12
+ }
13
+ var React__default = /*#__PURE__*/_interopDefault(React);
14
+ var Popover = function (_a) {
15
+ var buttonRef = _a.buttonRef,
16
+ popoverRef = _a.popoverRef,
17
+ racPopoverRef = _a.racPopoverRef,
18
+ children = _a.children;
19
+ var _b = SingleSelectContext.useSingleSelectContext(),
20
+ isOpen = _b.isOpen,
21
+ setOpen = _b.setOpen,
22
+ anchorName = _b.anchorName;
23
+ var _c = usePositioningStyles.usePositioningStyles(buttonRef, popoverRef, anchorName),
24
+ popoverStyle = _c.popoverStyle,
25
+ isPositioned = _c.isPositioned;
26
+ var shouldShowPopover = React.useMemo(function () {
27
+ return isOpen && isPositioned;
28
+ }, [isOpen, isPositioned]);
29
+ React.useLayoutEffect(function () {
30
+ var popover = popoverRef.current;
31
+ if (!(popover === null || popover === void 0 ? void 0 : popover.showPopover) || !(popover === null || popover === void 0 ? void 0 : popover.hidePopover)) return;
32
+ if (shouldShowPopover) {
33
+ popover.showPopover();
34
+ } else {
35
+ popover.hidePopover();
36
+ }
37
+ // eslint-disable-next-line react-hooks/exhaustive-deps
38
+ }, [shouldShowPopover]);
39
+ return React__default.default.createElement(reactAriaComponents.Popover, {
40
+ shouldUpdatePosition: false,
41
+ trigger: "manual",
42
+ isOpen: isOpen,
43
+ onOpenChange: setOpen,
44
+ ref: racPopoverRef
45
+ }, React__default.default.createElement("div", {
46
+ // @ts-expect-error - popover attribute is not included in current ts version, ignore type error
47
+ popover: "manual",
48
+ ref: popoverRef,
49
+ className: Popover_module.popover,
50
+ style: popoverStyle
51
+ }, children));
52
+ };
53
+ Popover.displayName = 'SingleSelect.Popover';
54
+ exports.Popover = Popover;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var styles = {
4
- "popover": "SingleSelect-module_popover__ZjL9n"
4
+ "popover": "Popover-module_popover__BjY2S"
5
5
  };
6
6
  module.exports = styles;
@@ -0,0 +1,94 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var React = require('react');
5
+ function usePopoverPositioning(_a) {
6
+ var triggerRef = _a.triggerRef,
7
+ popoverRef = _a.popoverRef,
8
+ _b = _a.direction,
9
+ direction = _b === void 0 ? 'ltr' : _b,
10
+ _c = _a.offset,
11
+ offset = _c === void 0 ? 4 : _c,
12
+ _d = _a.preferredPlacement,
13
+ preferredPlacement = _d === void 0 ? 'bottom' : _d;
14
+ var _e = React.useState({
15
+ top: preferredPlacement === 'bottom' ? offset : 'auto',
16
+ bottom: preferredPlacement === 'top' ? offset : 'auto',
17
+ insetInlineStart: 0,
18
+ maxHeight: 300 // TODO: update this based on designs
19
+ }),
20
+ position = _e[0],
21
+ setPosition = _e[1];
22
+ var _f = React.useState(true),
23
+ isPositioned = _f[0],
24
+ setIsPositioned = _f[1];
25
+ var mountedRef = React.useRef(false);
26
+ var isSSR = typeof window === 'undefined';
27
+ var updatePosition = React.useCallback(function () {
28
+ var _a;
29
+ if (isSSR) return;
30
+ var trigger = triggerRef.current;
31
+ var popover = popoverRef.current;
32
+ if (!mountedRef.current || !trigger || !(popover === null || popover === void 0 ? void 0 : popover.isConnected)) {
33
+ return;
34
+ }
35
+ var triggerRect = trigger.getBoundingClientRect();
36
+ if (!triggerRect) return;
37
+ var doc = trigger.ownerDocument;
38
+ var win = (_a = doc === null || doc === void 0 ? void 0 : doc.defaultView) !== null && _a !== void 0 ? _a : window;
39
+ var isRTL = direction === 'rtl';
40
+ var inlineStart = isRTL ? win.innerWidth - triggerRect.right : triggerRect.left;
41
+ var triggerTop = triggerRect.top;
42
+ var triggerBottom = triggerRect.bottom;
43
+ var viewportHeight = win.innerHeight;
44
+ var spaceAbove = triggerTop;
45
+ var spaceBelow = viewportHeight - triggerBottom;
46
+ var shouldFlip = preferredPlacement === 'bottom' && spaceBelow < 200 && spaceAbove > spaceBelow;
47
+ var top;
48
+ var bottom;
49
+ var maxHeight;
50
+ if (shouldFlip) {
51
+ top = 'auto';
52
+ bottom = viewportHeight - triggerTop + offset;
53
+ maxHeight = Math.max(0, spaceAbove - offset);
54
+ } else {
55
+ top = triggerBottom + offset;
56
+ bottom = 'auto';
57
+ maxHeight = Math.max(0, spaceBelow - offset);
58
+ }
59
+ var newPosition = {
60
+ top: top,
61
+ bottom: bottom,
62
+ insetInlineStart: inlineStart,
63
+ maxHeight: maxHeight
64
+ };
65
+ setPosition(newPosition);
66
+ setIsPositioned(true);
67
+ }, [triggerRef, popoverRef, direction, offset, preferredPlacement, isSSR]);
68
+ React.useEffect(function () {
69
+ if (typeof window === 'undefined') return;
70
+ mountedRef.current = true;
71
+ var triggerEl = triggerRef.current;
72
+ updatePosition();
73
+ var resizeObserver = new ResizeObserver(function () {
74
+ updatePosition();
75
+ });
76
+ if (triggerEl) resizeObserver.observe(triggerEl);
77
+ var onWindowResize = function () {
78
+ return updatePosition();
79
+ };
80
+ window.addEventListener('resize', onWindowResize, {
81
+ passive: true
82
+ });
83
+ return function () {
84
+ mountedRef.current = false;
85
+ resizeObserver.disconnect();
86
+ window.removeEventListener('resize', onWindowResize);
87
+ setIsPositioned(false);
88
+ };
89
+ }, [updatePosition, triggerRef]);
90
+ return tslib.__assign(tslib.__assign({}, position), {
91
+ isPositioned: isPositioned
92
+ });
93
+ }
94
+ exports.usePopoverPositioning = usePopoverPositioning;
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var i18n = require('@react-aria/i18n');
5
+ var usePopoverPositioning = require('./usePopoverPositioning.cjs');
6
+ var useSupportsAnchorPositioning = require('./useSupportsAnchorPositioning.cjs');
7
+ var CSS_PROPS = {
8
+ POSITION_ANCHOR: '--position-anchor',
9
+ POSITION_AREA: '--position-area'
10
+ };
11
+ var DEFAULTS = {
12
+ MAX_HEIGHT: '300px'
13
+ };
14
+ /**
15
+ * Generates manual positioning styles for browsers without anchor positioning support or SSR
16
+ */
17
+ var getManualPositioningStyles = function (positionData) {
18
+ return {
19
+ top: positionData.top,
20
+ bottom: positionData.bottom,
21
+ insetInlineStart: positionData.insetInlineStart,
22
+ maxHeight: positionData.maxHeight,
23
+ left: 'auto',
24
+ right: 'auto',
25
+ position: 'fixed'
26
+ };
27
+ };
28
+ var getAnchorPositioningStyles = function (anchorName, positionData) {
29
+ var _a;
30
+ var _b;
31
+ var styles = (_a = {
32
+ maxHeight: (_b = positionData.maxHeight) !== null && _b !== void 0 ? _b : DEFAULTS.MAX_HEIGHT
33
+ }, _a[CSS_PROPS.POSITION_ANCHOR] = anchorName, _a[CSS_PROPS.POSITION_AREA] = positionData.top === 'auto' ? 'top' : 'bottom', _a);
34
+ return styles;
35
+ };
36
+ var usePositioningStyles = function (buttonRef, popoverRef, anchorName) {
37
+ var direction = i18n.useLocale().direction;
38
+ var hasAnchorSupport = useSupportsAnchorPositioning.useSupportsAnchorPositioning();
39
+ var _a = usePopoverPositioning.usePopoverPositioning({
40
+ triggerRef: buttonRef,
41
+ popoverRef: popoverRef,
42
+ direction: direction,
43
+ preferredPlacement: 'bottom'
44
+ }),
45
+ top = _a.top,
46
+ bottom = _a.bottom,
47
+ insetInlineStart = _a.insetInlineStart,
48
+ maxHeight = _a.maxHeight,
49
+ isPositioned = _a.isPositioned;
50
+ var positionData = React.useMemo(function () {
51
+ return {
52
+ top: top,
53
+ bottom: bottom,
54
+ insetInlineStart: insetInlineStart,
55
+ maxHeight: maxHeight
56
+ };
57
+ }, [top, bottom, insetInlineStart, maxHeight]);
58
+ var popoverStyle = React.useMemo(function () {
59
+ if (hasAnchorSupport === null || !hasAnchorSupport) {
60
+ return getManualPositioningStyles(positionData);
61
+ }
62
+ return getAnchorPositioningStyles(anchorName, positionData);
63
+ }, [hasAnchorSupport, anchorName, positionData]);
64
+ return {
65
+ popoverStyle: popoverStyle,
66
+ isPositioned: isPositioned
67
+ };
68
+ };
69
+ exports.usePositioningStyles = usePositioningStyles;