@shopify/cli-kit 3.47.5 → 3.48.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.
Files changed (138) hide show
  1. package/assets/cli-ruby/lib/project_types/extension/messages/messages.rb +18 -1
  2. package/assets/cli-ruby/lib/shopify_cli/constants.rb +1 -0
  3. package/assets/cli-ruby/lib/shopify_cli/environment.rb +7 -0
  4. package/assets/cli-ruby/lib/shopify_cli/theme/extension/dev_server.rb +8 -2
  5. package/dist/private/node/ui/alert.js +3 -1
  6. package/dist/private/node/ui/alert.js.map +1 -1
  7. package/dist/private/node/ui/components/Alert.d.ts +1 -1
  8. package/dist/private/node/ui/components/Alert.js.map +1 -1
  9. package/dist/private/node/ui/components/AutocompletePrompt.d.ts +7 -2
  10. package/dist/private/node/ui/components/AutocompletePrompt.js +28 -91
  11. package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
  12. package/dist/private/node/ui/components/AutocompletePrompt.test.js +317 -257
  13. package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -1
  14. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +1 -0
  15. package/dist/private/node/ui/components/ConcurrentOutput.js +59 -32
  16. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  17. package/dist/private/node/ui/components/ConcurrentOutput.test.js +62 -22
  18. package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
  19. package/dist/private/node/ui/components/DangerousConfirmationPrompt.d.ts +12 -0
  20. package/dist/private/node/ui/components/DangerousConfirmationPrompt.js +77 -0
  21. package/dist/private/node/ui/components/DangerousConfirmationPrompt.js.map +1 -0
  22. package/dist/private/node/ui/components/DangerousConfirmationPrompt.test.js +101 -0
  23. package/dist/private/node/ui/components/DangerousConfirmationPrompt.test.js.map +1 -0
  24. package/dist/private/node/ui/components/List.d.ts +1 -0
  25. package/dist/private/node/ui/components/List.js +2 -2
  26. package/dist/private/node/ui/components/List.js.map +1 -1
  27. package/dist/private/node/ui/components/{GitDiff.d.ts → Prompts/GitDiff.d.ts} +4 -2
  28. package/dist/private/node/ui/components/{GitDiff.js → Prompts/GitDiff.js} +3 -2
  29. package/dist/private/node/ui/components/Prompts/GitDiff.js.map +1 -0
  30. package/dist/private/node/ui/components/Prompts/GitDiff.test.d.ts +1 -0
  31. package/dist/private/node/ui/components/{GitDiff.test.js → Prompts/GitDiff.test.js} +50 -28
  32. package/dist/private/node/ui/components/Prompts/GitDiff.test.js.map +1 -0
  33. package/dist/private/node/ui/components/Prompts/InfoMessage.d.ts +14 -0
  34. package/dist/private/node/ui/components/Prompts/InfoMessage.js +11 -0
  35. package/dist/private/node/ui/components/Prompts/InfoMessage.js.map +1 -0
  36. package/dist/private/node/ui/components/Prompts/InfoMessage.test.d.ts +1 -0
  37. package/dist/private/node/ui/components/Prompts/InfoMessage.test.js +21 -0
  38. package/dist/private/node/ui/components/Prompts/InfoMessage.test.js.map +1 -0
  39. package/dist/private/node/ui/components/Prompts/InfoTable.d.ts +1 -0
  40. package/dist/private/node/ui/components/Prompts/InfoTable.js +11 -7
  41. package/dist/private/node/ui/components/Prompts/InfoTable.js.map +1 -1
  42. package/dist/private/node/ui/components/Prompts/InfoTable.test.js +6 -4
  43. package/dist/private/node/ui/components/Prompts/InfoTable.test.js.map +1 -1
  44. package/dist/private/node/ui/components/Prompts/PromptLayout.d.ts +21 -0
  45. package/dist/private/node/ui/components/Prompts/PromptLayout.js +73 -0
  46. package/dist/private/node/ui/components/Prompts/PromptLayout.js.map +1 -0
  47. package/dist/private/node/ui/components/Prompts/PromptLayout.test.d.ts +1 -0
  48. package/dist/private/node/ui/components/Prompts/PromptLayout.test.js +129 -0
  49. package/dist/private/node/ui/components/Prompts/PromptLayout.test.js.map +1 -0
  50. package/dist/private/node/ui/components/Scrollbar.d.ts +10 -0
  51. package/dist/private/node/ui/components/Scrollbar.js +44 -0
  52. package/dist/private/node/ui/components/Scrollbar.js.map +1 -0
  53. package/dist/private/node/ui/components/Scrollbar.test.d.ts +1 -0
  54. package/dist/private/node/ui/components/Scrollbar.test.js +96 -0
  55. package/dist/private/node/ui/components/Scrollbar.test.js.map +1 -0
  56. package/dist/private/node/ui/components/SelectInput.d.ts +3 -6
  57. package/dist/private/node/ui/components/SelectInput.js +57 -41
  58. package/dist/private/node/ui/components/SelectInput.js.map +1 -1
  59. package/dist/private/node/ui/components/SelectInput.test.js +120 -192
  60. package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
  61. package/dist/private/node/ui/components/SelectPrompt.d.ts +7 -6
  62. package/dist/private/node/ui/components/SelectPrompt.js +11 -68
  63. package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
  64. package/dist/private/node/ui/components/SelectPrompt.test.js +135 -65
  65. package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
  66. package/dist/private/node/ui/components/Table/Row.js +2 -1
  67. package/dist/private/node/ui/components/Table/Row.js.map +1 -1
  68. package/dist/private/node/ui/components/Table/Table.js +2 -1
  69. package/dist/private/node/ui/components/Table/Table.js.map +1 -1
  70. package/dist/private/node/ui/components/Tasks.js +1 -8
  71. package/dist/private/node/ui/components/Tasks.js.map +1 -1
  72. package/dist/private/node/ui/components/TextInput.d.ts +1 -0
  73. package/dist/private/node/ui/components/TextInput.js +10 -4
  74. package/dist/private/node/ui/components/TextInput.js.map +1 -1
  75. package/dist/private/node/ui/components/TextInput.test.js +27 -18
  76. package/dist/private/node/ui/components/TextInput.test.js.map +1 -1
  77. package/dist/private/node/ui/components/TextPrompt.d.ts +2 -3
  78. package/dist/private/node/ui/components/TextPrompt.js +18 -16
  79. package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
  80. package/dist/private/node/ui/components/TextPrompt.test.js +25 -11
  81. package/dist/private/node/ui/components/TextPrompt.test.js.map +1 -1
  82. package/dist/private/node/ui/hooks/use-prompt.d.ts +18 -0
  83. package/dist/private/node/ui/hooks/use-prompt.js +20 -0
  84. package/dist/private/node/ui/hooks/use-prompt.js.map +1 -0
  85. package/dist/private/node/ui/hooks/use-select-state.d.ts +4 -4
  86. package/dist/private/node/ui/hooks/use-select-state.js +9 -9
  87. package/dist/private/node/ui/hooks/use-select-state.js.map +1 -1
  88. package/dist/public/common/object.d.ts +16 -0
  89. package/dist/public/common/object.js +26 -0
  90. package/dist/public/common/object.js.map +1 -1
  91. package/dist/public/common/string.d.ts +16 -0
  92. package/dist/public/common/string.js +30 -0
  93. package/dist/public/common/string.js.map +1 -1
  94. package/dist/public/common/version.d.ts +1 -1
  95. package/dist/public/common/version.js +1 -1
  96. package/dist/public/common/version.js.map +1 -1
  97. package/dist/public/node/analytics.js +3 -1
  98. package/dist/public/node/analytics.js.map +1 -1
  99. package/dist/public/node/base-command.d.ts +1 -0
  100. package/dist/public/node/base-command.js +21 -1
  101. package/dist/public/node/base-command.js.map +1 -1
  102. package/dist/public/node/figures.d.ts +2 -0
  103. package/dist/public/node/figures.js +3 -0
  104. package/dist/public/node/figures.js.map +1 -0
  105. package/dist/public/node/metadata.d.ts +2 -1
  106. package/dist/public/node/metadata.js +5 -2
  107. package/dist/public/node/metadata.js.map +1 -1
  108. package/dist/public/node/monorail.d.ts +22 -1
  109. package/dist/public/node/monorail.js +1 -1
  110. package/dist/public/node/monorail.js.map +1 -1
  111. package/dist/public/node/output.d.ts +1 -1
  112. package/dist/public/node/output.js +1 -1
  113. package/dist/public/node/output.js.map +1 -1
  114. package/dist/public/node/ruby.d.ts +1 -0
  115. package/dist/public/node/ruby.js +1 -0
  116. package/dist/public/node/ruby.js.map +1 -1
  117. package/dist/public/node/system.js +2 -2
  118. package/dist/public/node/system.js.map +1 -1
  119. package/dist/public/node/themes/models/theme.d.ts +2 -1
  120. package/dist/public/node/themes/models/theme.js +2 -1
  121. package/dist/public/node/themes/models/theme.js.map +1 -1
  122. package/dist/public/node/themes/theme-urls.d.ts +1 -0
  123. package/dist/public/node/themes/theme-urls.js +4 -0
  124. package/dist/public/node/themes/theme-urls.js.map +1 -1
  125. package/dist/public/node/themes/themes-api.d.ts +9 -1
  126. package/dist/public/node/themes/themes-api.js +14 -3
  127. package/dist/public/node/themes/themes-api.js.map +1 -1
  128. package/dist/public/node/toml.d.ts +3 -2
  129. package/dist/public/node/toml.js +5 -2
  130. package/dist/public/node/toml.js.map +1 -1
  131. package/dist/public/node/ui.d.ts +82 -27
  132. package/dist/public/node/ui.js +97 -32
  133. package/dist/public/node/ui.js.map +1 -1
  134. package/dist/tsconfig.tsbuildinfo +1 -1
  135. package/package.json +14 -14
  136. package/dist/private/node/ui/components/GitDiff.js.map +0 -1
  137. package/dist/private/node/ui/components/GitDiff.test.js.map +0 -1
  138. /package/dist/private/node/ui/components/{GitDiff.test.d.ts → DangerousConfirmationPrompt.test.d.ts} +0 -0
@@ -4,10 +4,11 @@ import { handleCtrlC } from '../../ui.js';
4
4
  import useLayout from '../hooks/use-layout.js';
5
5
  import { messageWithPunctuation } from '../utilities.js';
6
6
  import useAbortSignal from '../hooks/use-abort-signal.js';
7
+ import usePrompt, { PromptState } from '../hooks/use-prompt.js';
7
8
  import React, { useCallback, useState } from 'react';
8
9
  import { Box, useApp, useInput, Text } from 'ink';
9
10
  import figures from 'figures';
10
- const TextPrompt = ({ message, onSubmit, validate, defaultValue = '', password = false, allowEmpty = false, emptyDisplayedValue = '(empty)', abortSignal, previewPrefix, previewValue, previewSuffix, }) => {
11
+ const TextPrompt = ({ message, onSubmit, validate, defaultValue = '', password = false, allowEmpty = false, emptyDisplayedValue = '(empty)', abortSignal, preview, }) => {
11
12
  if (password && defaultValue) {
12
13
  throw new Error("Can't use defaultValue with password");
13
14
  }
@@ -20,24 +21,27 @@ const TextPrompt = ({ message, onSubmit, validate, defaultValue = '', password =
20
21
  return undefined;
21
22
  }, [allowEmpty, validate]);
22
23
  const { oneThird } = useLayout();
23
- const [answer, setAnswer] = useState('');
24
+ const { promptState, setPromptState, answer, setAnswer } = usePrompt({
25
+ initialAnswer: '',
26
+ });
24
27
  const answerOrDefault = answer.length > 0 ? answer : defaultValue;
25
28
  const displayEmptyValue = answerOrDefault === '';
26
29
  const displayedAnswer = displayEmptyValue ? emptyDisplayedValue : answerOrDefault;
27
30
  const { exit: unmountInk } = useApp();
28
- const [submitted, setSubmitted] = useState(false);
29
31
  const [error, setError] = useState(undefined);
30
- const shouldShowError = submitted && error;
31
- const color = shouldShowError ? 'red' : 'cyan';
32
+ const color = promptState === PromptState.Error ? 'red' : 'cyan';
32
33
  const underline = new Array(oneThird - 3).fill('▔');
33
34
  const { isAborted } = useAbortSignal(abortSignal);
34
35
  useInput((input, key) => {
35
36
  handleCtrlC(input, key);
36
37
  if (key.return) {
37
- setSubmitted(true);
38
38
  const error = validateAnswer(answerOrDefault);
39
- setError(error);
40
- if (!error) {
39
+ if (error) {
40
+ setPromptState(PromptState.Error);
41
+ setError(error);
42
+ }
43
+ else {
44
+ setPromptState(PromptState.Submitted);
41
45
  onSubmit(answerOrDefault);
42
46
  unmountInk();
43
47
  }
@@ -48,7 +52,7 @@ const TextPrompt = ({ message, onSubmit, validate, defaultValue = '', password =
48
52
  React.createElement(Box, { marginRight: 2 },
49
53
  React.createElement(Text, null, "?")),
50
54
  React.createElement(TokenizedText, { item: messageWithPunctuation(message) })),
51
- submitted && !error ? (React.createElement(Box, null,
55
+ promptState === PromptState.Submitted ? (React.createElement(Box, null,
52
56
  React.createElement(Box, { marginRight: 2 },
53
57
  React.createElement(Text, { color: "cyan" }, figures.tick)),
54
58
  React.createElement(Box, { flexGrow: 1 },
@@ -59,16 +63,14 @@ const TextPrompt = ({ message, onSubmit, validate, defaultValue = '', password =
59
63
  React.createElement(Box, { flexGrow: 1 },
60
64
  React.createElement(TextInput, { value: answer, onChange: (answer) => {
61
65
  setAnswer(answer);
62
- setSubmitted(false);
66
+ setPromptState(PromptState.Idle);
63
67
  }, defaultValue: defaultValue, color: color, password: password }))),
64
68
  React.createElement(Box, { marginLeft: 3 },
65
69
  React.createElement(Text, { color: color }, underline)),
66
- shouldShowError ? (React.createElement(Box, { marginLeft: 3 },
67
- React.createElement(Text, { color: color }, error))) : null)),
68
- previewValue && !submitted ? (React.createElement(Box, { marginLeft: 3 },
69
- React.createElement(Text, null, previewPrefix ? previewPrefix(answerOrDefault) : null),
70
- React.createElement(Text, { color: color }, previewValue(answerOrDefault)),
71
- React.createElement(Text, null, previewSuffix ? previewSuffix(answerOrDefault) : null))) : null));
70
+ promptState === PromptState.Error ? (React.createElement(Box, { marginLeft: 3 },
71
+ React.createElement(Text, { color: color }, error))) : null,
72
+ promptState !== PromptState.Error && preview ? (React.createElement(Box, { marginLeft: 3 },
73
+ React.createElement(TokenizedText, { item: preview(answerOrDefault) }))) : null))));
72
74
  };
73
75
  export { TextPrompt };
74
76
  //# sourceMappingURL=TextPrompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {TokenizedText} from './TokenizedText.js'\nimport {handleCtrlC} from '../../ui.js'\nimport useLayout from '../hooks/use-layout.js'\nimport {messageWithPunctuation} from '../utilities.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport useAbortSignal from '../hooks/use-abort-signal.js'\nimport React, {FunctionComponent, useCallback, useState} from 'react'\nimport {Box, useApp, useInput, Text} from 'ink'\nimport figures from 'figures'\n\nexport interface TextPromptProps {\n message: string\n onSubmit: (value: string) => void\n defaultValue?: string\n password?: boolean\n validate?: (value: string) => string | undefined\n allowEmpty?: boolean\n emptyDisplayedValue?: string\n abortSignal?: AbortSignal\n previewPrefix?: (value: string) => string | undefined\n previewValue?: (value: string) => string | undefined\n previewSuffix?: (value: string) => string | undefined\n}\n\nconst TextPrompt: FunctionComponent<TextPromptProps> = ({\n message,\n onSubmit,\n validate,\n defaultValue = '',\n password = false,\n allowEmpty = false,\n emptyDisplayedValue = '(empty)',\n abortSignal,\n previewPrefix,\n previewValue,\n previewSuffix,\n}) => {\n if (password && defaultValue) {\n throw new Error(\"Can't use defaultValue with password\")\n }\n\n const validateAnswer = useCallback(\n (value: string): string | undefined => {\n if (validate) {\n return validate(value)\n }\n\n if (value.length === 0 && !allowEmpty) return 'Type an answer to the prompt.'\n\n return undefined\n },\n [allowEmpty, validate],\n )\n\n const {oneThird} = useLayout()\n const [answer, setAnswer] = useState<string>('')\n const answerOrDefault = answer.length > 0 ? answer : defaultValue\n const displayEmptyValue = answerOrDefault === ''\n const displayedAnswer = displayEmptyValue ? emptyDisplayedValue : answerOrDefault\n const {exit: unmountInk} = useApp()\n const [submitted, setSubmitted] = useState(false)\n const [error, setError] = useState<string | undefined>(undefined)\n const shouldShowError = submitted && error\n const color = shouldShowError ? 'red' : 'cyan'\n const underline = new Array(oneThird - 3).fill('▔')\n const {isAborted} = useAbortSignal(abortSignal)\n\n useInput((input, key) => {\n handleCtrlC(input, key)\n\n if (key.return) {\n setSubmitted(true)\n const error = validateAnswer(answerOrDefault)\n setError(error)\n\n if (!error) {\n onSubmit(answerOrDefault)\n unmountInk()\n }\n }\n })\n\n return isAborted ? null : (\n <Box flexDirection=\"column\" marginBottom={1} width={oneThird}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <TokenizedText item={messageWithPunctuation(message)} />\n </Box>\n {submitted && !error ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Box flexGrow={1}>\n <Text color=\"cyan\" dimColor={displayEmptyValue}>\n {password ? '*'.repeat(answer.length) : displayedAnswer}\n </Text>\n </Box>\n </Box>\n ) : (\n <Box flexDirection=\"column\">\n <Box>\n <Box marginRight={2}>\n <Text color={color}>{`>`}</Text>\n </Box>\n <Box flexGrow={1}>\n <TextInput\n value={answer}\n onChange={(answer) => {\n setAnswer(answer)\n setSubmitted(false)\n }}\n defaultValue={defaultValue}\n color={color}\n password={password}\n />\n </Box>\n </Box>\n <Box marginLeft={3}>\n <Text color={color}>{underline}</Text>\n </Box>\n {shouldShowError ? (\n <Box marginLeft={3}>\n <Text color={color}>{error}</Text>\n </Box>\n ) : null}\n </Box>\n )}\n {previewValue && !submitted ? (\n <Box marginLeft={3}>\n <Text>{previewPrefix ? previewPrefix(answerOrDefault) : null}</Text>\n <Text color={color}>{previewValue(answerOrDefault)}</Text>\n <Text>{previewSuffix ? previewSuffix(answerOrDefault) : null}</Text>\n </Box>\n ) : null}\n </Box>\n )\n}\n\nexport {TextPrompt}\n"]}
1
+ {"version":3,"file":"TextPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {InlineToken, TokenItem, TokenizedText} from './TokenizedText.js'\nimport {handleCtrlC} from '../../ui.js'\nimport useLayout from '../hooks/use-layout.js'\nimport {messageWithPunctuation} from '../utilities.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport useAbortSignal from '../hooks/use-abort-signal.js'\nimport usePrompt, {PromptState} from '../hooks/use-prompt.js'\nimport React, {FunctionComponent, useCallback, useState} from 'react'\nimport {Box, useApp, useInput, Text} from 'ink'\nimport figures from 'figures'\n\nexport interface TextPromptProps {\n message: string\n onSubmit: (value: string) => void\n defaultValue?: string\n password?: boolean\n validate?: (value: string) => string | undefined\n allowEmpty?: boolean\n emptyDisplayedValue?: string\n abortSignal?: AbortSignal\n preview?: (value: string) => TokenItem<InlineToken>\n}\n\nconst TextPrompt: FunctionComponent<TextPromptProps> = ({\n message,\n onSubmit,\n validate,\n defaultValue = '',\n password = false,\n allowEmpty = false,\n emptyDisplayedValue = '(empty)',\n abortSignal,\n preview,\n}) => {\n if (password && defaultValue) {\n throw new Error(\"Can't use defaultValue with password\")\n }\n\n const validateAnswer = useCallback(\n (value: string): string | undefined => {\n if (validate) {\n return validate(value)\n }\n\n if (value.length === 0 && !allowEmpty) return 'Type an answer to the prompt.'\n\n return undefined\n },\n [allowEmpty, validate],\n )\n\n const {oneThird} = useLayout()\n const {promptState, setPromptState, answer, setAnswer} = usePrompt<string>({\n initialAnswer: '',\n })\n const answerOrDefault = answer.length > 0 ? answer : defaultValue\n const displayEmptyValue = answerOrDefault === ''\n const displayedAnswer = displayEmptyValue ? emptyDisplayedValue : answerOrDefault\n const {exit: unmountInk} = useApp()\n const [error, setError] = useState<string | undefined>(undefined)\n const color = promptState === PromptState.Error ? 'red' : 'cyan'\n const underline = new Array(oneThird - 3).fill('▔')\n const {isAborted} = useAbortSignal(abortSignal)\n\n useInput((input, key) => {\n handleCtrlC(input, key)\n\n if (key.return) {\n const error = validateAnswer(answerOrDefault)\n\n if (error) {\n setPromptState(PromptState.Error)\n setError(error)\n } else {\n setPromptState(PromptState.Submitted)\n onSubmit(answerOrDefault)\n unmountInk()\n }\n }\n })\n\n return isAborted ? null : (\n <Box flexDirection=\"column\" marginBottom={1} width={oneThird}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <TokenizedText item={messageWithPunctuation(message)} />\n </Box>\n {promptState === PromptState.Submitted ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Box flexGrow={1}>\n <Text color=\"cyan\" dimColor={displayEmptyValue}>\n {password ? '*'.repeat(answer.length) : displayedAnswer}\n </Text>\n </Box>\n </Box>\n ) : (\n <Box flexDirection=\"column\">\n <Box>\n <Box marginRight={2}>\n <Text color={color}>{`>`}</Text>\n </Box>\n <Box flexGrow={1}>\n <TextInput\n value={answer}\n onChange={(answer) => {\n setAnswer(answer)\n setPromptState(PromptState.Idle)\n }}\n defaultValue={defaultValue}\n color={color}\n password={password}\n />\n </Box>\n </Box>\n <Box marginLeft={3}>\n <Text color={color}>{underline}</Text>\n </Box>\n {promptState === PromptState.Error ? (\n <Box marginLeft={3}>\n <Text color={color}>{error}</Text>\n </Box>\n ) : null}\n {promptState !== PromptState.Error && preview ? (\n <Box marginLeft={3}>\n <TokenizedText item={preview(answerOrDefault)} />\n </Box>\n ) : null}\n </Box>\n )}\n </Box>\n )\n}\n\nexport {TextPrompt}\n"]}
@@ -4,6 +4,7 @@ import { unstyled } from '../../../../public/node/output.js';
4
4
  import { AbortController } from '../../../../public/node/abort.js';
5
5
  import React from 'react';
6
6
  import { describe, expect, test, vi } from 'vitest';
7
+ import colors from '@shopify/cli-kit/node/colors';
7
8
  const ENTER = '\r';
8
9
  describe('TextPrompt', () => {
9
10
  test('default state', () => {
@@ -22,7 +23,7 @@ describe('TextPrompt', () => {
22
23
  // testing with styles because the color changes to red
23
24
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
24
25
  "? Test question:
25
- >  
26
+ > █
26
27
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
27
28
  Type an answer to the prompt.
28
29
  "
@@ -31,7 +32,7 @@ describe('TextPrompt', () => {
31
32
  // color changes back to valid color
32
33
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
33
34
  "? Test question:
34
- > A 
35
+ > A█
35
36
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
36
37
  "
37
38
  `);
@@ -44,7 +45,7 @@ describe('TextPrompt', () => {
44
45
  // testing with styles because the color changes to red
45
46
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
46
47
  "? Test question:
47
- > this-test-includes-shopify 
48
+ > this-test-includes-shopify█
48
49
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
49
50
  App name can't include the word shopify
50
51
  "
@@ -109,7 +110,7 @@ describe('TextPrompt', () => {
109
110
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
110
111
  "? Test question:
111
112
  > AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
112
- BBBBBB 
113
+ BBBBBB█
113
114
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
114
115
  "
115
116
  `);
@@ -120,7 +121,7 @@ describe('TextPrompt', () => {
120
121
  await sendInputAndWaitForChange(renderInstance, 'ABC');
121
122
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
122
123
  "? Test question:
123
- > *** 
124
+ > ***█
124
125
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
125
126
  "
126
127
  `);
@@ -135,7 +136,7 @@ describe('TextPrompt', () => {
135
136
  const { lastFrame } = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question?" }));
136
137
  expect(lastFrame()).toMatchInlineSnapshot(`
137
138
  "? Test question?
138
- >  
139
+ > █
139
140
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
140
141
  "
141
142
  `);
@@ -153,14 +154,27 @@ describe('TextPrompt', () => {
153
154
  await expect(promise).resolves.toEqual(undefined);
154
155
  });
155
156
  test('shows a preview footer when provided', async () => {
156
- const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "How tall are you in cm?", previewPrefix: () => 'You are ', previewValue: (value) => String(Number(value) / 100), previewSuffix: () => 'm tall.' }));
157
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "How tall are you in cm?", preview: (value) => `You are ${colors.cyan(String(Number(value) / 100))}m tall.` }));
157
158
  await waitForInputsToBeReady();
158
159
  await sendInputAndWaitForChange(renderInstance, '180');
159
- expect(unstyled(renderInstance.lastFrame())).toMatchInlineSnapshot(`
160
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
160
161
  "? How tall are you in cm?
161
- > 180
162
- ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
163
- You are 1.8m tall.
162
+ > 180█
163
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
164
+ You are 1.8m tall.
165
+ "
166
+ `);
167
+ });
168
+ test('the preview footer wraps when the value is very long', async () => {
169
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "How tall are you?", preview: (value) => `You are ${colors.cyan(`incredibly humongously savagely unnaturally monstrously pathetically arrogantly ${value}`)} tall.` }));
170
+ await waitForInputsToBeReady();
171
+ await sendInputAndWaitForChange(renderInstance, 'uber');
172
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
173
+ "? How tall are you?
174
+ > uber█
175
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
176
+ You are incredibly humongously savagely unnaturally monstrously pathetically 
177
+ arrogantly uber tall.
164
178
  "
165
179
  `);
166
180
  });
@@ -1 +1 @@
1
- {"version":3,"file":"TextPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextPrompt} from './TextPrompt.js'\nimport {getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady, render} from '../../testing/ui.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {AbortController} from '../../../../public/node/abort.js'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\n\nconst ENTER = '\\r'\n\ndescribe('TextPrompt', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" defaultValue=\"Placeholder\" />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question:\n > Placeholder\n ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n \"\n `)\n })\n\n test('default validation error', async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[31m>\u001b[39m \u001b[31m\u001b[7m \u001b[27m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mType an answer to the prompt.\u001b[39m\n \"\n `)\n await sendInputAndWaitForChange(renderInstance, 'A')\n // color changes back to valid color\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36mA\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test('custom validation error', async () => {\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"Test question\"\n validate={(value) => (value.includes('shopify') ? \"App name can't include the word shopify\" : undefined)}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'this-test-includes-shopify')\n await sendInputAndWaitForChange(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[31m>\u001b[39m \u001b[31mthis-test-includes-shopify\u001b[7m \u001b[27m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mApp name can't include the word shopify\u001b[39m\n \"\n `)\n })\n\n test('submitting the value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A')\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('submitting the default value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" defaultValue=\"A\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('display the empty value when no input is entered and there is no default value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(\n <TextPrompt onSubmit={onSubmit} message=\"Test question\" allowEmpty emptyDisplayedValue=\"empty\" />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ empty\n \"\n `)\n })\n\n test(\"display the default value when allow empty is enabled but the user don't modify it\", async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(\n <TextPrompt\n onSubmit={onSubmit}\n message=\"Test question\"\n allowEmpty\n emptyDisplayedValue=\"empty\"\n defaultValue=\"A\"\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('text wrapping', async () => {\n // component width is 80 characters wide in tests but because of the question mark and\n // spaces before the question, we only have 77 characters to work with\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A'.repeat(77))\n await sendInputAndWaitForChange(renderInstance, 'B'.repeat(6))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[39m\n \u001b[36mBBBBBB\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test(\"masking the input if it's a password\", async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" password />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'ABC')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36m***\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ ***\n \"\n `)\n })\n\n test(\"doesn't append a colon to the message if it ends with a question mark\", async () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question?\" />)\n\n expect(lastFrame()!).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m>\u001b[39m \u001b[36m\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test(\"doesn't allow to pass defaultValue and password at the same time\", async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" password defaultValue=\"A\" />)\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toContain(\"ERROR Can't use defaultValue with password\")\n })\n\n test('abortController can be used to exit the prompt from outside', async () => {\n const abortController = new AbortController()\n\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"Test question\"\n defaultValue=\"Placeholder\"\n abortSignal={abortController.signal}\n />,\n )\n const promise = renderInstance.waitUntilExit()\n\n abortController.abort()\n\n expect(getLastFrameAfterUnmount(renderInstance)).toEqual('')\n await expect(promise).resolves.toEqual(undefined)\n })\n\n test('shows a preview footer when provided', async () => {\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"How tall are you in cm?\"\n previewPrefix={() => 'You are '}\n previewValue={(value) => String(Number(value) / 100)}\n previewSuffix={() => 'm tall.'}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, '180')\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? How tall are you in cm?\n > 180 \n ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n You are 1.8m tall.\n \"\n `)\n })\n})\n"]}
1
+ {"version":3,"file":"TextPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextPrompt} from './TextPrompt.js'\nimport {getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady, render} from '../../testing/ui.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {AbortController} from '../../../../public/node/abort.js'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\nimport colors from '@shopify/cli-kit/node/colors'\n\nconst ENTER = '\\r'\n\ndescribe('TextPrompt', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" defaultValue=\"Placeholder\" />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question:\n > Placeholder\n ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n \"\n `)\n })\n\n test('default validation error', async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[31m>\u001b[39m \u001b[31m\u001b[41m█\u001b[49m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mType an answer to the prompt.\u001b[39m\n \"\n `)\n await sendInputAndWaitForChange(renderInstance, 'A')\n // color changes back to valid color\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36mA\u001b[46m█\u001b[49m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test('custom validation error', async () => {\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"Test question\"\n validate={(value) => (value.includes('shopify') ? \"App name can't include the word shopify\" : undefined)}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'this-test-includes-shopify')\n await sendInputAndWaitForChange(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[31m>\u001b[39m \u001b[31mthis-test-includes-shopify\u001b[41m█\u001b[49m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mApp name can't include the word shopify\u001b[39m\n \"\n `)\n })\n\n test('submitting the value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A')\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('submitting the default value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" defaultValue=\"A\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('display the empty value when no input is entered and there is no default value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(\n <TextPrompt onSubmit={onSubmit} message=\"Test question\" allowEmpty emptyDisplayedValue=\"empty\" />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ empty\n \"\n `)\n })\n\n test(\"display the default value when allow empty is enabled but the user don't modify it\", async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(\n <TextPrompt\n onSubmit={onSubmit}\n message=\"Test question\"\n allowEmpty\n emptyDisplayedValue=\"empty\"\n defaultValue=\"A\"\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('text wrapping', async () => {\n // component width is 80 characters wide in tests but because of the question mark and\n // spaces before the question, we only have 77 characters to work with\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A'.repeat(77))\n await sendInputAndWaitForChange(renderInstance, 'B'.repeat(6))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[39m\n \u001b[36mBBBBBB\u001b[46m█\u001b[49m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test(\"masking the input if it's a password\", async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" password />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'ABC')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36m***\u001b[46m█\u001b[49m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ ***\n \"\n `)\n })\n\n test(\"doesn't append a colon to the message if it ends with a question mark\", async () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question?\" />)\n\n expect(lastFrame()!).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m>\u001b[39m \u001b[36m\u001b[46m█\u001b[49m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test(\"doesn't allow to pass defaultValue and password at the same time\", async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" password defaultValue=\"A\" />)\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toContain(\"ERROR Can't use defaultValue with password\")\n })\n\n test('abortController can be used to exit the prompt from outside', async () => {\n const abortController = new AbortController()\n\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"Test question\"\n defaultValue=\"Placeholder\"\n abortSignal={abortController.signal}\n />,\n )\n const promise = renderInstance.waitUntilExit()\n\n abortController.abort()\n\n expect(getLastFrameAfterUnmount(renderInstance)).toEqual('')\n await expect(promise).resolves.toEqual(undefined)\n })\n\n test('shows a preview footer when provided', async () => {\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"How tall are you in cm?\"\n preview={(value) => `You are ${colors.cyan(String(Number(value) / 100))}m tall.`}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, '180')\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? How tall are you in cm?\n \u001b[36m>\u001b[39m \u001b[36m180\u001b[46m█\u001b[49m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n You are \u001b[36m1.8\u001b[39mm tall.\n \"\n `)\n })\n\n test('the preview footer wraps when the value is very long', async () => {\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"How tall are you?\"\n preview={(value) =>\n `You are ${colors.cyan(\n `incredibly humongously savagely unnaturally monstrously pathetically arrogantly ${value}`,\n )} tall.`\n }\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'uber')\n\n expect(renderInstance.lastFrame()!).toMatchInlineSnapshot(`\n \"? How tall are you?\n \u001b[36m>\u001b[39m \u001b[36muber\u001b[46m█\u001b[49m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n You are \u001b[36mincredibly humongously savagely unnaturally monstrously pathetically \u001b[39m\n \u001b[36marrogantly uber\u001b[39m tall.\n \"\n `)\n })\n})\n"]}
@@ -0,0 +1,18 @@
1
+ /// <reference types="react" />
2
+ export declare enum PromptState {
3
+ Idle = "idle",
4
+ Loading = "loading",
5
+ Submitted = "submitted",
6
+ Error = "error",
7
+ Cancelled = "cancelled"
8
+ }
9
+ interface UsePromptProps<T> {
10
+ initialAnswer: T;
11
+ }
12
+ export default function usePrompt<T>({ initialAnswer }: UsePromptProps<T>): {
13
+ promptState: PromptState;
14
+ setPromptState: import("react").Dispatch<import("react").SetStateAction<PromptState>>;
15
+ answer: T;
16
+ setAnswer: import("react").Dispatch<import("react").SetStateAction<T>>;
17
+ };
18
+ export {};
@@ -0,0 +1,20 @@
1
+ import { useState } from 'react';
2
+ export var PromptState;
3
+ (function (PromptState) {
4
+ PromptState["Idle"] = "idle";
5
+ PromptState["Loading"] = "loading";
6
+ PromptState["Submitted"] = "submitted";
7
+ PromptState["Error"] = "error";
8
+ PromptState["Cancelled"] = "cancelled";
9
+ })(PromptState || (PromptState = {}));
10
+ export default function usePrompt({ initialAnswer }) {
11
+ const [promptState, setPromptState] = useState(PromptState.Idle);
12
+ const [answer, setAnswer] = useState(initialAnswer);
13
+ return {
14
+ promptState,
15
+ setPromptState,
16
+ answer,
17
+ setAnswer,
18
+ };
19
+ }
20
+ //# sourceMappingURL=use-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-prompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/hooks/use-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAA;AAE9B,MAAM,CAAN,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,kCAAmB,CAAA;IACnB,sCAAuB,CAAA;IACvB,8BAAe,CAAA;IACf,sCAAuB,CAAA;AACzB,CAAC,EANW,WAAW,KAAX,WAAW,QAMtB;AAMD,MAAM,CAAC,OAAO,UAAU,SAAS,CAAI,EAAC,aAAa,EAAoB;IACrE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAc,WAAW,CAAC,IAAI,CAAC,CAAA;IAC7E,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAI,aAAa,CAAC,CAAA;IAEtD,OAAO;QACL,WAAW;QACX,cAAc;QACd,MAAM;QACN,SAAS;KACV,CAAA;AACH,CAAC","sourcesContent":["import {useState} from 'react'\n\nexport enum PromptState {\n Idle = 'idle',\n Loading = 'loading',\n Submitted = 'submitted',\n Error = 'error',\n Cancelled = 'cancelled',\n}\n\ninterface UsePromptProps<T> {\n initialAnswer: T\n}\n\nexport default function usePrompt<T>({initialAnswer}: UsePromptProps<T>) {\n const [promptState, setPromptState] = useState<PromptState>(PromptState.Idle)\n const [answer, setAnswer] = useState<T>(initialAnswer)\n\n return {\n promptState,\n setPromptState,\n answer,\n setAnswer,\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { ItemWithKey } from '../components/SelectInput.js';
2
- type Option<T> = ItemWithKey<T>;
1
+ import { Item } from '../components/SelectInput.js';
2
+ type Option<T> = Item<T>;
3
3
  type OptionMapItem<T> = Option<T> & {
4
4
  previous: OptionMapItem<T> | undefined;
5
5
  next: OptionMapItem<T> | undefined;
@@ -40,7 +40,7 @@ export interface UseSelectStateProps<T> {
40
40
  * Number of items to display.
41
41
  *
42
42
  */
43
- visibleOptionCount?: number;
43
+ visibleOptionCount: number;
44
44
  /**
45
45
  * Options.
46
46
  */
@@ -50,7 +50,7 @@ export interface UseSelectStateProps<T> {
50
50
  */
51
51
  defaultValue?: T;
52
52
  }
53
- export type SelectState<T> = Pick<State<T>, 'visibleFromIndex' | 'visibleToIndex' | 'value'> & {
53
+ export type SelectState<T> = Pick<State<T>, 'visibleOptionCount' | 'visibleFromIndex' | 'visibleToIndex' | 'value'> & {
54
54
  /**
55
55
  * Visible options.
56
56
  */
@@ -44,15 +44,15 @@ const reducer = (state, action) => {
44
44
  if (!next) {
45
45
  return state;
46
46
  }
47
- const needsToScroll = next.index >= state.visibleToIndex;
47
+ const needsToScroll = next.index > state.visibleToIndex;
48
48
  if (!needsToScroll) {
49
49
  return {
50
50
  ...state,
51
51
  value: next.value,
52
52
  };
53
53
  }
54
- const nextVisibleToIndex = Math.min(state.optionMap.size, state.visibleToIndex + 1);
55
- const nextVisibleFromIndex = nextVisibleToIndex - state.visibleOptionCount;
54
+ const nextVisibleToIndex = next.index;
55
+ const nextVisibleFromIndex = nextVisibleToIndex - state.visibleOptionCount + 1;
56
56
  return {
57
57
  ...state,
58
58
  value: next.value,
@@ -76,15 +76,15 @@ const reducer = (state, action) => {
76
76
  if (!previous) {
77
77
  return state;
78
78
  }
79
- const needsToScroll = previous.index <= state.visibleFromIndex;
79
+ const needsToScroll = previous.index < state.visibleFromIndex;
80
80
  if (!needsToScroll) {
81
81
  return {
82
82
  ...state,
83
83
  value: previous.value,
84
84
  };
85
85
  }
86
- const nextVisibleFromIndex = Math.max(0, state.visibleFromIndex - 1);
87
- const nextVisibleToIndex = nextVisibleFromIndex + state.visibleOptionCount;
86
+ const nextVisibleFromIndex = previous.index;
87
+ const nextVisibleToIndex = nextVisibleFromIndex + state.visibleOptionCount - 1;
88
88
  return {
89
89
  ...state,
90
90
  value: previous.value,
@@ -124,7 +124,7 @@ const createDefaultState = ({ visibleOptionCount: customVisibleOptionCount, defa
124
124
  optionMap,
125
125
  visibleOptionCount,
126
126
  visibleFromIndex: 0,
127
- visibleToIndex: visibleOptionCount,
127
+ visibleToIndex: visibleOptionCount - 1,
128
128
  value: option?.value,
129
129
  previousValue: option?.value,
130
130
  };
@@ -164,8 +164,8 @@ export const useSelectState = ({ visibleOptionCount, options, defaultValue }) =>
164
164
  });
165
165
  }, []);
166
166
  const visibleOptions = useMemo(() => {
167
- return options.slice(state.visibleFromIndex, state.visibleToIndex);
168
- }, [options, state.visibleFromIndex, state.visibleToIndex]);
167
+ return options.slice(state.visibleFromIndex);
168
+ }, [options, state.visibleFromIndex]);
169
169
  return {
170
170
  visibleFromIndex: state.visibleFromIndex,
171
171
  visibleToIndex: state.visibleToIndex,
@@ -1 +1 @@
1
- {"version":3,"file":"use-select-state.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/hooks/use-select-state.ts"],"names":[],"mappings":"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","sourcesContent":["import {ItemWithKey} from '../components/SelectInput.js'\nimport {useReducer, useCallback, useMemo, useState} from 'react'\nimport {isDeepStrictEqual} from 'node:util'\n\ntype Option<T> = ItemWithKey<T>\n\ntype OptionMapItem<T> = Option<T> & {\n previous: OptionMapItem<T> | undefined\n next: OptionMapItem<T> | undefined\n index: number\n}\n\nexport default class OptionMap<T> extends Map<T, OptionMapItem<T>> {\n readonly first: OptionMapItem<T> | undefined\n\n constructor(options: Option<T>[]) {\n const items: [T, OptionMapItem<T>][] = []\n let firstItem: OptionMapItem<T> | undefined\n let previous: OptionMapItem<T> | undefined\n let index = 0\n\n for (const option of options) {\n const item = {\n ...option,\n previous,\n next: undefined,\n index,\n }\n\n if (previous) {\n previous.next = item\n }\n\n if (!firstItem) {\n firstItem = item\n }\n\n items.push([option.value, item])\n index++\n previous = item\n }\n\n super(items)\n this.first = firstItem\n }\n}\n\ninterface State<T> {\n /**\n * Map where key is option's value and value is option's index.\n */\n optionMap: OptionMap<T>\n\n /**\n * Number of visible options.\n */\n visibleOptionCount: number\n\n /**\n * Index of the first visible option.\n */\n visibleFromIndex: number\n\n /**\n * Index of the last visible option.\n */\n visibleToIndex: number\n\n /**\n * Value of the previously selected option.\n */\n previousValue: T | undefined\n\n /**\n * Value of the selected option.\n */\n value: T | undefined\n}\n\ntype Action<T> = SelectNextOptionAction<T> | SelectPreviousOptionAction<T> | SelectOptionAction<T> | ResetAction<T>\n\ninterface SelectNextOptionAction<T> {\n type: 'select-next-option'\n}\n\ninterface SelectPreviousOptionAction<T> {\n type: 'select-previous-option'\n}\n\ninterface SelectOptionAction<T> {\n type: 'select-option'\n option: Option<T>\n}\n\ninterface ResetAction<T> {\n type: 'reset'\n state: State<T>\n}\n\nconst reducer = <T>(state: State<T>, action: Action<T>): State<T> => {\n switch (action.type) {\n case 'select-next-option': {\n if (typeof state.value === 'undefined') {\n return state\n }\n\n const item = state.optionMap.get(state.value)\n\n if (!item) {\n return state\n }\n\n let next = item.next\n\n while (next && next.disabled) {\n next = next.next\n }\n\n if (!next) {\n return state\n }\n\n const needsToScroll = next.index >= state.visibleToIndex\n\n if (!needsToScroll) {\n return {\n ...state,\n value: next.value,\n }\n }\n\n const nextVisibleToIndex = Math.min(state.optionMap.size, state.visibleToIndex + 1)\n const nextVisibleFromIndex = nextVisibleToIndex - state.visibleOptionCount\n\n return {\n ...state,\n value: next.value,\n visibleFromIndex: nextVisibleFromIndex,\n visibleToIndex: nextVisibleToIndex,\n previousValue: state.value,\n }\n }\n\n case 'select-previous-option': {\n if (typeof state.value === 'undefined') {\n return state\n }\n\n const item = state.optionMap.get(state.value)\n\n if (!item) {\n return state\n }\n\n let previous = item.previous\n\n while (previous && previous.disabled) {\n previous = previous.previous\n }\n\n if (!previous) {\n return state\n }\n\n const needsToScroll = previous.index <= state.visibleFromIndex\n\n if (!needsToScroll) {\n return {\n ...state,\n value: previous.value,\n }\n }\n\n const nextVisibleFromIndex = Math.max(0, state.visibleFromIndex - 1)\n const nextVisibleToIndex = nextVisibleFromIndex + state.visibleOptionCount\n\n return {\n ...state,\n value: previous.value,\n visibleFromIndex: nextVisibleFromIndex,\n visibleToIndex: nextVisibleToIndex,\n previousValue: state.value,\n }\n }\n\n case 'select-option': {\n const item = state.optionMap.get(action.option.value)\n\n if (!item) {\n return state\n }\n\n return {\n ...state,\n value: item.value,\n previousValue: state.value,\n }\n }\n\n case 'reset': {\n return action.state\n }\n\n default: {\n return state\n }\n }\n}\n\nexport interface UseSelectStateProps<T> {\n /**\n * Number of items to display.\n *\n */\n visibleOptionCount?: number\n\n /**\n * Options.\n */\n options: Option<T>[]\n\n /**\n * Initially selected option's value.\n */\n defaultValue?: T\n}\n\nexport type SelectState<T> = Pick<State<T>, 'visibleFromIndex' | 'visibleToIndex' | 'value'> & {\n /**\n * Visible options.\n */\n visibleOptions: (Option<T> & {index: number})[]\n\n /**\n * Select next option and scroll the list down, if needed.\n */\n selectNextOption: () => void\n\n /**\n * Select previous option and scroll the list up, if needed.\n */\n selectPreviousOption: () => void\n\n /**\n * Select option directly.\n */\n selectOption: (option: Option<T>) => void\n}\n\ntype CreateDefaultStateProps<T> = Pick<UseSelectStateProps<T>, 'visibleOptionCount' | 'defaultValue' | 'options'>\n\nconst createDefaultState = <T>({\n visibleOptionCount: customVisibleOptionCount,\n defaultValue,\n options,\n}: CreateDefaultStateProps<T>) => {\n const visibleOptionCount =\n typeof customVisibleOptionCount === 'number' ? Math.min(customVisibleOptionCount, options.length) : options.length\n\n const optionMap = new OptionMap(options)\n\n const defaultOption = typeof defaultValue === 'undefined' ? undefined : optionMap.get(defaultValue)\n\n let option = defaultOption && !defaultOption.disabled ? defaultOption : optionMap.first\n\n while (option && option.disabled) {\n option = option.next\n }\n\n return {\n optionMap,\n visibleOptionCount,\n visibleFromIndex: 0,\n visibleToIndex: visibleOptionCount,\n value: option?.value,\n previousValue: option?.value,\n }\n}\n\nexport const useSelectState = <T>({visibleOptionCount, options, defaultValue}: UseSelectStateProps<T>) => {\n const [state, dispatch] = useReducer(reducer, {visibleOptionCount, defaultValue, options}, createDefaultState)\n const [lastOptions, setLastOptions] = useState(options)\n const [lastVisibleOptionCount, setLastVisibleOptionCount] = useState(visibleOptionCount)\n\n if (options !== lastOptions && !isDeepStrictEqual(options, lastOptions)) {\n dispatch({\n type: 'reset',\n state: createDefaultState({visibleOptionCount, defaultValue, options}),\n })\n\n setLastOptions(options)\n }\n\n if (visibleOptionCount !== lastVisibleOptionCount) {\n dispatch({\n type: 'reset',\n state: createDefaultState({visibleOptionCount, defaultValue, options}),\n })\n\n setLastVisibleOptionCount(visibleOptionCount)\n }\n\n const selectNextOption = useCallback(() => {\n dispatch({\n type: 'select-next-option',\n })\n }, [])\n\n const selectPreviousOption = useCallback(() => {\n dispatch({\n type: 'select-previous-option',\n })\n }, [])\n\n const selectOption = useCallback(({option}: {option: Option<T>}) => {\n dispatch({\n type: 'select-option',\n option,\n })\n }, [])\n\n const visibleOptions = useMemo(() => {\n return options.slice(state.visibleFromIndex, state.visibleToIndex)\n }, [options, state.visibleFromIndex, state.visibleToIndex])\n\n return {\n visibleFromIndex: state.visibleFromIndex,\n visibleToIndex: state.visibleToIndex,\n value: state.value,\n visibleOptions,\n selectNextOption,\n selectPreviousOption,\n selectOption,\n previousValue: state.previousValue,\n }\n}\n"]}
1
+ {"version":3,"file":"use-select-state.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/hooks/use-select-state.ts"],"names":[],"mappings":"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","sourcesContent":["import {Item} from '../components/SelectInput.js'\nimport {useReducer, useCallback, useMemo, useState} from 'react'\nimport {isDeepStrictEqual} from 'node:util'\n\ntype Option<T> = Item<T>\n\ntype OptionMapItem<T> = Option<T> & {\n previous: OptionMapItem<T> | undefined\n next: OptionMapItem<T> | undefined\n index: number\n}\n\nexport default class OptionMap<T> extends Map<T, OptionMapItem<T>> {\n readonly first: OptionMapItem<T> | undefined\n\n constructor(options: Option<T>[]) {\n const items: [T, OptionMapItem<T>][] = []\n let firstItem: OptionMapItem<T> | undefined\n let previous: OptionMapItem<T> | undefined\n let index = 0\n\n for (const option of options) {\n const item = {\n ...option,\n previous,\n next: undefined,\n index,\n }\n\n if (previous) {\n previous.next = item\n }\n\n if (!firstItem) {\n firstItem = item\n }\n\n items.push([option.value, item])\n index++\n previous = item\n }\n\n super(items)\n this.first = firstItem\n }\n}\n\ninterface State<T> {\n /**\n * Map where key is option's value and value is option's index.\n */\n optionMap: OptionMap<T>\n\n /**\n * Number of visible options.\n */\n visibleOptionCount: number\n\n /**\n * Index of the first visible option.\n */\n visibleFromIndex: number\n\n /**\n * Index of the last visible option.\n */\n visibleToIndex: number\n\n /**\n * Value of the previously selected option.\n */\n previousValue: T | undefined\n\n /**\n * Value of the selected option.\n */\n value: T | undefined\n}\n\ntype Action<T> = SelectNextOptionAction<T> | SelectPreviousOptionAction<T> | SelectOptionAction<T> | ResetAction<T>\n\ninterface SelectNextOptionAction<T> {\n type: 'select-next-option'\n}\n\ninterface SelectPreviousOptionAction<T> {\n type: 'select-previous-option'\n}\n\ninterface SelectOptionAction<T> {\n type: 'select-option'\n option: Option<T>\n}\n\ninterface ResetAction<T> {\n type: 'reset'\n state: State<T>\n}\n\nconst reducer = <T>(state: State<T>, action: Action<T>): State<T> => {\n switch (action.type) {\n case 'select-next-option': {\n if (typeof state.value === 'undefined') {\n return state\n }\n\n const item = state.optionMap.get(state.value)\n\n if (!item) {\n return state\n }\n\n let next = item.next\n\n while (next && next.disabled) {\n next = next.next\n }\n\n if (!next) {\n return state\n }\n\n const needsToScroll = next.index > state.visibleToIndex\n\n if (!needsToScroll) {\n return {\n ...state,\n value: next.value,\n }\n }\n\n const nextVisibleToIndex = next.index\n const nextVisibleFromIndex = nextVisibleToIndex - state.visibleOptionCount + 1\n\n return {\n ...state,\n value: next.value,\n visibleFromIndex: nextVisibleFromIndex,\n visibleToIndex: nextVisibleToIndex,\n previousValue: state.value,\n }\n }\n\n case 'select-previous-option': {\n if (typeof state.value === 'undefined') {\n return state\n }\n\n const item = state.optionMap.get(state.value)\n\n if (!item) {\n return state\n }\n\n let previous = item.previous\n\n while (previous && previous.disabled) {\n previous = previous.previous\n }\n\n if (!previous) {\n return state\n }\n\n const needsToScroll = previous.index < state.visibleFromIndex\n\n if (!needsToScroll) {\n return {\n ...state,\n value: previous.value,\n }\n }\n\n const nextVisibleFromIndex = previous.index\n const nextVisibleToIndex = nextVisibleFromIndex + state.visibleOptionCount - 1\n\n return {\n ...state,\n value: previous.value,\n visibleFromIndex: nextVisibleFromIndex,\n visibleToIndex: nextVisibleToIndex,\n previousValue: state.value,\n }\n }\n\n case 'select-option': {\n const item = state.optionMap.get(action.option.value)\n\n if (!item) {\n return state\n }\n\n return {\n ...state,\n value: item.value,\n previousValue: state.value,\n }\n }\n\n case 'reset': {\n return action.state\n }\n\n default: {\n return state\n }\n }\n}\n\nexport interface UseSelectStateProps<T> {\n /**\n * Number of items to display.\n *\n */\n visibleOptionCount: number\n\n /**\n * Options.\n */\n options: Option<T>[]\n\n /**\n * Initially selected option's value.\n */\n defaultValue?: T\n}\n\nexport type SelectState<T> = Pick<State<T>, 'visibleOptionCount' | 'visibleFromIndex' | 'visibleToIndex' | 'value'> & {\n /**\n * Visible options.\n */\n visibleOptions: (Option<T> & {index: number})[]\n\n /**\n * Select next option and scroll the list down, if needed.\n */\n selectNextOption: () => void\n\n /**\n * Select previous option and scroll the list up, if needed.\n */\n selectPreviousOption: () => void\n\n /**\n * Select option directly.\n */\n selectOption: (option: Option<T>) => void\n}\n\ntype CreateDefaultStateProps<T> = Pick<UseSelectStateProps<T>, 'visibleOptionCount' | 'defaultValue' | 'options'>\n\nconst createDefaultState = <T>({\n visibleOptionCount: customVisibleOptionCount,\n defaultValue,\n options,\n}: CreateDefaultStateProps<T>) => {\n const visibleOptionCount =\n typeof customVisibleOptionCount === 'number' ? Math.min(customVisibleOptionCount, options.length) : options.length\n const optionMap = new OptionMap(options)\n const defaultOption = typeof defaultValue === 'undefined' ? undefined : optionMap.get(defaultValue)\n let option = defaultOption && !defaultOption.disabled ? defaultOption : optionMap.first\n\n while (option && option.disabled) {\n option = option.next\n }\n\n return {\n optionMap,\n visibleOptionCount,\n visibleFromIndex: 0,\n visibleToIndex: visibleOptionCount - 1,\n value: option?.value,\n previousValue: option?.value,\n }\n}\n\nexport const useSelectState = <T>({visibleOptionCount, options, defaultValue}: UseSelectStateProps<T>) => {\n const [state, dispatch] = useReducer(reducer, {visibleOptionCount, defaultValue, options}, createDefaultState)\n const [lastOptions, setLastOptions] = useState(options)\n const [lastVisibleOptionCount, setLastVisibleOptionCount] = useState(visibleOptionCount)\n\n if (options !== lastOptions && !isDeepStrictEqual(options, lastOptions)) {\n dispatch({\n type: 'reset',\n state: createDefaultState({visibleOptionCount, defaultValue, options}),\n })\n\n setLastOptions(options)\n }\n\n if (visibleOptionCount !== lastVisibleOptionCount) {\n dispatch({\n type: 'reset',\n state: createDefaultState({visibleOptionCount, defaultValue, options}),\n })\n\n setLastVisibleOptionCount(visibleOptionCount)\n }\n\n const selectNextOption = useCallback(() => {\n dispatch({\n type: 'select-next-option',\n })\n }, [])\n\n const selectPreviousOption = useCallback(() => {\n dispatch({\n type: 'select-previous-option',\n })\n }, [])\n\n const selectOption = useCallback(({option}: {option: Option<T>}) => {\n dispatch({\n type: 'select-option',\n option,\n })\n }, [])\n\n const visibleOptions = useMemo(() => {\n return options.slice(state.visibleFromIndex)\n }, [options, state.visibleFromIndex])\n\n return {\n visibleFromIndex: state.visibleFromIndex,\n visibleToIndex: state.visibleToIndex,\n value: state.value,\n visibleOptions,\n selectNextOption,\n selectPreviousOption,\n selectOption,\n previousValue: state.previousValue,\n }\n}\n"]}
@@ -30,3 +30,19 @@ export declare function pickBy<T, S extends T>(object: Dictionary<T> | null | un
30
30
  export declare function mapValues<T extends object, TResult>(source: T | null | undefined, callback: ObjectIterator<T, TResult>): {
31
31
  [P in keyof T]: TResult;
32
32
  };
33
+ /**
34
+ * Deeply compares two objects and returns true if they are equal.
35
+ *
36
+ * @param one - The first object to be compared.
37
+ * @param two - The second object to be compared.
38
+ * @returns True if the objects are equal, false otherwise.
39
+ */
40
+ export declare function deepCompare(one: object, two: object): boolean;
41
+ /**
42
+ * Return the difference between two nested objects.
43
+ *
44
+ * @param one - The first object to be compared.
45
+ * @param two - The second object to be compared.
46
+ * @returns Two objects containing the fields that are different, each one with the values of one object.
47
+ */
48
+ export declare function deepDifference(one: object, two: object): [object, object];
@@ -40,4 +40,30 @@ export function mapValues(source, callback) {
40
40
  const lodashMapValues = require('lodash/mapValues.js');
41
41
  return lodashMapValues(source, callback);
42
42
  }
43
+ /**
44
+ * Deeply compares two objects and returns true if they are equal.
45
+ *
46
+ * @param one - The first object to be compared.
47
+ * @param two - The second object to be compared.
48
+ * @returns True if the objects are equal, false otherwise.
49
+ */
50
+ export function deepCompare(one, two) {
51
+ const lodashIsEqual = require('lodash/isEqual.js');
52
+ return lodashIsEqual(one, two);
53
+ }
54
+ /**
55
+ * Return the difference between two nested objects.
56
+ *
57
+ * @param one - The first object to be compared.
58
+ * @param two - The second object to be compared.
59
+ * @returns Two objects containing the fields that are different, each one with the values of one object.
60
+ */
61
+ export function deepDifference(one, two) {
62
+ const differenceWith = require('lodash/differenceWith.js');
63
+ const fromPairs = require('lodash/fromPairs.js');
64
+ const toPairs = require('lodash/toPairs.js');
65
+ const changes = differenceWith(toPairs(one), toPairs(two), deepCompare);
66
+ const changes2 = differenceWith(toPairs(two), toPairs(one), deepCompare);
67
+ return [fromPairs(changes), fromPairs(changes2)];
68
+ }
43
69
  //# sourceMappingURL=object.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"object.js","sourceRoot":"","sources":["../../../src/public/common/object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,kBAAkB,EAAC,MAAM,+BAA+B,CAAA;AAChE,OAAO,SAAS,MAAM,WAAW,CAAA;AAEjC,OAAO,EAAC,aAAa,EAAC,MAAM,QAAQ,CAAA;AAEpC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAgB,EAChB,GAAgB,EAChB,qBAAyF,kBAAkB;IAE3G,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAC,UAAU,EAAE,kBAAkB,EAAC,CAAC,CAAA;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,gEAAgE;AAChE,MAAM,UAAU,MAAM,CACpB,MAAwC,EACxC,SAA+B;IAE/B,MAAM,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAChD,OAAO,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AACxC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CACvB,MAA4B,EAC5B,QAAoC;IAEpC,MAAM,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACtD,OAAO,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAC1C,CAAC","sourcesContent":["import {unionArrayStrategy} from '../../private/common/array.js'\nimport deepMerge from 'deepmerge'\nimport {Dictionary, ObjectIterator, ValueKeyIteratee} from 'lodash'\nimport {createRequire} from 'module'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Deep merges the two objects and returns a new object with the merge result.\n *\n * @param lhs - One of the objects to be merged.\n * @param rhs - Another object to be merged.\n * @param arrayMergeStrategy - Strategy used to merge the array typed fields. Union strategy is used by default to avoid\n * duplicated elements.\n * @returns A Javascrip tobject with th emerged objects.\n */\nexport function deepMergeObjects<T1, T2>(\n lhs: Partial<T1>,\n rhs: Partial<T2>,\n arrayMergeStrategy: (destinationArray: unknown[], sourceArray: unknown[]) => unknown[] = unionArrayStrategy,\n): T1 & T2 {\n return deepMerge(lhs, rhs, {arrayMerge: arrayMergeStrategy})\n}\n\n/**\n * Creates an object composed of the `object` properties `predicate` returns\n * truthy for. The predicate is invoked with two arguments: (value, key).\n *\n * @param object - The source object.\n * @param predicate - The function invoked per property.\n * @returns Returns the new object.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport function pickBy<T, S extends T>(\n object: Dictionary<T> | null | undefined,\n predicate?: ValueKeyIteratee<T>,\n): Dictionary<S> {\n const lodashPickBy = require('lodash/pickBy.js')\n return lodashPickBy(object, predicate)\n}\n\n/**\n * Creates an object with the same keys as object and values generated by running each own\n * enumerable property of object through iteratee. The iteratee function is\n * invoked with three arguments: (value, key, object).\n *\n * @param source - The object to iterate over.\n * @param callback - The function invoked per iteration.\n * @returns Returns the new mapped object.\n */\nexport function mapValues<T extends object, TResult>(\n source: T | null | undefined,\n callback: ObjectIterator<T, TResult>,\n): {[P in keyof T]: TResult} {\n const lodashMapValues = require('lodash/mapValues.js')\n return lodashMapValues(source, callback)\n}\n"]}
1
+ {"version":3,"file":"object.js","sourceRoot":"","sources":["../../../src/public/common/object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,kBAAkB,EAAC,MAAM,+BAA+B,CAAA;AAChE,OAAO,SAAS,MAAM,WAAW,CAAA;AAEjC,OAAO,EAAC,aAAa,EAAC,MAAM,QAAQ,CAAA;AAEpC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAgB,EAChB,GAAgB,EAChB,qBAAyF,kBAAkB;IAE3G,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAC,UAAU,EAAE,kBAAkB,EAAC,CAAC,CAAA;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,gEAAgE;AAChE,MAAM,UAAU,MAAM,CACpB,MAAwC,EACxC,SAA+B;IAE/B,MAAM,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAChD,OAAO,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AACxC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CACvB,MAA4B,EAC5B,QAAoC;IAEpC,MAAM,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACtD,OAAO,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW;IAClD,MAAM,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAClD,OAAO,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW;IACrD,MAAM,cAAc,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAA;IAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC5C,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,CAAA;IACvE,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,CAAA;IACxE,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;AAClD,CAAC","sourcesContent":["import {unionArrayStrategy} from '../../private/common/array.js'\nimport deepMerge from 'deepmerge'\nimport {Dictionary, ObjectIterator, ValueKeyIteratee} from 'lodash'\nimport {createRequire} from 'module'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Deep merges the two objects and returns a new object with the merge result.\n *\n * @param lhs - One of the objects to be merged.\n * @param rhs - Another object to be merged.\n * @param arrayMergeStrategy - Strategy used to merge the array typed fields. Union strategy is used by default to avoid\n * duplicated elements.\n * @returns A Javascrip tobject with th emerged objects.\n */\nexport function deepMergeObjects<T1, T2>(\n lhs: Partial<T1>,\n rhs: Partial<T2>,\n arrayMergeStrategy: (destinationArray: unknown[], sourceArray: unknown[]) => unknown[] = unionArrayStrategy,\n): T1 & T2 {\n return deepMerge(lhs, rhs, {arrayMerge: arrayMergeStrategy})\n}\n\n/**\n * Creates an object composed of the `object` properties `predicate` returns\n * truthy for. The predicate is invoked with two arguments: (value, key).\n *\n * @param object - The source object.\n * @param predicate - The function invoked per property.\n * @returns Returns the new object.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport function pickBy<T, S extends T>(\n object: Dictionary<T> | null | undefined,\n predicate?: ValueKeyIteratee<T>,\n): Dictionary<S> {\n const lodashPickBy = require('lodash/pickBy.js')\n return lodashPickBy(object, predicate)\n}\n\n/**\n * Creates an object with the same keys as object and values generated by running each own\n * enumerable property of object through iteratee. The iteratee function is\n * invoked with three arguments: (value, key, object).\n *\n * @param source - The object to iterate over.\n * @param callback - The function invoked per iteration.\n * @returns Returns the new mapped object.\n */\nexport function mapValues<T extends object, TResult>(\n source: T | null | undefined,\n callback: ObjectIterator<T, TResult>,\n): {[P in keyof T]: TResult} {\n const lodashMapValues = require('lodash/mapValues.js')\n return lodashMapValues(source, callback)\n}\n\n/**\n * Deeply compares two objects and returns true if they are equal.\n *\n * @param one - The first object to be compared.\n * @param two - The second object to be compared.\n * @returns True if the objects are equal, false otherwise.\n */\nexport function deepCompare(one: object, two: object): boolean {\n const lodashIsEqual = require('lodash/isEqual.js')\n return lodashIsEqual(one, two)\n}\n\n/**\n * Return the difference between two nested objects.\n *\n * @param one - The first object to be compared.\n * @param two - The second object to be compared.\n * @returns Two objects containing the fields that are different, each one with the values of one object.\n */\nexport function deepDifference(one: object, two: object): [object, object] {\n const differenceWith = require('lodash/differenceWith.js')\n const fromPairs = require('lodash/fromPairs.js')\n const toPairs = require('lodash/toPairs.js')\n const changes = differenceWith(toPairs(one), toPairs(two), deepCompare)\n const changes2 = differenceWith(toPairs(two), toPairs(one), deepCompare)\n return [fromPairs(changes), fromPairs(changes2)]\n}\n"]}
@@ -81,3 +81,19 @@ export declare function underscore(input: string): string;
81
81
  * @returns The transformed string.
82
82
  */
83
83
  export declare function constantize(input: string): string;
84
+ /**
85
+ * Given a date, return a formatted string like "2021-01-01 12:00:00".
86
+ *
87
+ * @param date - Date to format.
88
+ * @returns The transformed string.
89
+ */
90
+ export declare function formatDate(date: Date): string;
91
+ /**
92
+ * Given a list of items, it returns a string with the items joined by commas and the last item joined by "and".
93
+ * All items are wrapped in double quotes.
94
+ * For example: ["a", "b", "c"] returns "a", "b" and "c".
95
+ *
96
+ * @param items - List of items.
97
+ * @returns The joined string.
98
+ */
99
+ export declare function joinWithAnd(items: string[]): string;