@fpkit/acss 0.4.4

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 (297) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/chunk-77CZU5XZ.cjs +9 -0
  4. package/dist/chunk-77CZU5XZ.cjs.map +1 -0
  5. package/dist/chunk-D43FJIRQ.cjs +31 -0
  6. package/dist/chunk-D43FJIRQ.cjs.map +1 -0
  7. package/dist/chunk-GJWMCDFS.js +9 -0
  8. package/dist/chunk-GJWMCDFS.js.map +1 -0
  9. package/dist/chunk-PCDUGD3C.js +5 -0
  10. package/dist/chunk-PCDUGD3C.js.map +1 -0
  11. package/dist/hooks.cjs +10 -0
  12. package/dist/hooks.cjs.map +1 -0
  13. package/dist/hooks.d.cts +32 -0
  14. package/dist/hooks.d.ts +32 -0
  15. package/dist/hooks.js +8 -0
  16. package/dist/hooks.js.map +1 -0
  17. package/dist/icon-e6044c73.d.ts +227 -0
  18. package/dist/icons.cjs +73 -0
  19. package/dist/icons.cjs.map +1 -0
  20. package/dist/icons.d.cts +252 -0
  21. package/dist/icons.d.ts +252 -0
  22. package/dist/icons.js +4 -0
  23. package/dist/icons.js.map +1 -0
  24. package/dist/index.cjs +59 -0
  25. package/dist/index.cjs.map +1 -0
  26. package/dist/index.d.cts +566 -0
  27. package/dist/index.d.ts +566 -0
  28. package/dist/index.js +11 -0
  29. package/dist/index.js.map +1 -0
  30. package/libs/chunk-GCGKYLDG.js +7 -0
  31. package/libs/chunk-GCGKYLDG.js.map +1 -0
  32. package/libs/chunk-PDD4N5P5.cjs +10 -0
  33. package/libs/chunk-PDD4N5P5.cjs.map +1 -0
  34. package/libs/chunk-QHIABQNQ.js +8 -0
  35. package/libs/chunk-QHIABQNQ.js.map +1 -0
  36. package/libs/chunk-ZOHIKF6I.cjs +31 -0
  37. package/libs/chunk-ZOHIKF6I.cjs.map +1 -0
  38. package/libs/components/badge/badge.css +1 -0
  39. package/libs/components/badge/badge.css.map +1 -0
  40. package/libs/components/badge/badge.min.css +3 -0
  41. package/libs/components/breadcrumbs/breadcrumb.css +1 -0
  42. package/libs/components/breadcrumbs/breadcrumb.css.map +1 -0
  43. package/libs/components/breadcrumbs/breadcrumb.min.css +3 -0
  44. package/libs/components/buttons/button.css +1 -0
  45. package/libs/components/buttons/button.css.map +1 -0
  46. package/libs/components/buttons/button.min.css +3 -0
  47. package/libs/components/cards/card-style.css +1 -0
  48. package/libs/components/cards/card-style.css.map +1 -0
  49. package/libs/components/cards/card-style.min.css +3 -0
  50. package/libs/components/cards/card.css +1 -0
  51. package/libs/components/cards/card.css.map +1 -0
  52. package/libs/components/cards/card.min.css +3 -0
  53. package/libs/components/details/details.css +1 -0
  54. package/libs/components/details/details.css.map +1 -0
  55. package/libs/components/details/details.min.css +3 -0
  56. package/libs/components/form/form.css +1 -0
  57. package/libs/components/form/form.css.map +1 -0
  58. package/libs/components/form/form.min.css +3 -0
  59. package/libs/components/icons/icon.css +1 -0
  60. package/libs/components/icons/icon.css.map +1 -0
  61. package/libs/components/icons/icon.min.css +3 -0
  62. package/libs/components/images/img.css +1 -0
  63. package/libs/components/images/img.css.map +1 -0
  64. package/libs/components/images/img.min.css +3 -0
  65. package/libs/components/layout/landmarks.css +1 -0
  66. package/libs/components/layout/landmarks.css.map +1 -0
  67. package/libs/components/layout/landmarks.min.css +3 -0
  68. package/libs/components/link/link.css +1 -0
  69. package/libs/components/link/link.css.map +1 -0
  70. package/libs/components/link/link.min.css +3 -0
  71. package/libs/components/nav/nav.css +1 -0
  72. package/libs/components/nav/nav.css.map +1 -0
  73. package/libs/components/nav/nav.min.css +3 -0
  74. package/libs/components/progress/progress.css +1 -0
  75. package/libs/components/progress/progress.css.map +1 -0
  76. package/libs/components/progress/progress.min.css +3 -0
  77. package/libs/components/styles/index.css +1 -0
  78. package/libs/components/styles/index.css.map +1 -0
  79. package/libs/components/styles/index.min.css +3 -0
  80. package/libs/components/tag/tag.css +1 -0
  81. package/libs/components/tag/tag.css.map +1 -0
  82. package/libs/components/tag/tag.min.css +3 -0
  83. package/libs/components/text-to-speech/text-to-speech.css +1 -0
  84. package/libs/components/text-to-speech/text-to-speech.css.map +1 -0
  85. package/libs/components/text-to-speech/text-to-speech.min.css +3 -0
  86. package/libs/hooks.cjs +12 -0
  87. package/libs/hooks.cjs.map +1 -0
  88. package/libs/hooks.d.cts +32 -0
  89. package/libs/hooks.d.ts +32 -0
  90. package/libs/hooks.js +3 -0
  91. package/libs/hooks.js.map +1 -0
  92. package/libs/icons-1f5afc0c.d.ts +318 -0
  93. package/libs/icons.cjs +12 -0
  94. package/libs/icons.cjs.map +1 -0
  95. package/libs/icons.d.cts +2 -0
  96. package/libs/icons.d.ts +2 -0
  97. package/libs/icons.js +3 -0
  98. package/libs/icons.js.map +1 -0
  99. package/libs/index.cjs +71 -0
  100. package/libs/index.cjs.map +1 -0
  101. package/libs/index.css +1 -0
  102. package/libs/index.css.map +1 -0
  103. package/libs/index.d.cts +551 -0
  104. package/libs/index.d.ts +551 -0
  105. package/libs/index.js +11 -0
  106. package/libs/index.js.map +1 -0
  107. package/package.json +125 -0
  108. package/src/App.css +42 -0
  109. package/src/App.tsx +35 -0
  110. package/src/__snapshots__/App.test.tsx.snap +56 -0
  111. package/src/components/.gitkeep +0 -0
  112. package/src/components/__snapshots__/fp.test.tsx.snap +3 -0
  113. package/src/components/badge/badge.scss +20 -0
  114. package/src/components/badge/badge.stories.tsx +54 -0
  115. package/src/components/badge/badge.tsx +17 -0
  116. package/src/components/breadcrumbs/bc-item.tsx +20 -0
  117. package/src/components/breadcrumbs/breadcrumb.scss +35 -0
  118. package/src/components/breadcrumbs/breadcrumb.stories.tsx +92 -0
  119. package/src/components/breadcrumbs/breadcrumb.tsx +218 -0
  120. package/src/components/buttons/button.scss +115 -0
  121. package/src/components/buttons/button.stories.tsx +57 -0
  122. package/src/components/buttons/button.test.tsx +104 -0
  123. package/src/components/buttons/button.tsx +64 -0
  124. package/src/components/cards/card-style.scss +0 -0
  125. package/src/components/cards/card.scss +43 -0
  126. package/src/components/cards/card.stories.tsx +114 -0
  127. package/src/components/cards/card.test.tsx +30 -0
  128. package/src/components/cards/card.tsx +135 -0
  129. package/src/components/cards/flex-card.tsx +15 -0
  130. package/src/components/details/details.scss +75 -0
  131. package/src/components/details/details.stories.tsx +122 -0
  132. package/src/components/details/details.tsx +77 -0
  133. package/src/components/form/README.mdx +70 -0
  134. package/src/components/form/fields.tsx +45 -0
  135. package/src/components/form/form.scss +87 -0
  136. package/src/components/form/form.stories.tsx +49 -0
  137. package/src/components/form/form.tsx +71 -0
  138. package/src/components/form/input.stories.tsx +155 -0
  139. package/src/components/form/inputs.tsx +84 -0
  140. package/src/components/form/select.stories.tsx +38 -0
  141. package/src/components/form/select.tsx +112 -0
  142. package/src/components/form/textarea.tsx +87 -0
  143. package/src/components/fp.test.tsx +56 -0
  144. package/src/components/fp.tsx +78 -0
  145. package/src/components/heading/heading.stories.tsx +75 -0
  146. package/src/components/heading/heading.tsx +27 -0
  147. package/src/components/icons/components/add.tsx +42 -0
  148. package/src/components/icons/components/arrow-down.tsx +52 -0
  149. package/src/components/icons/components/arrow-left.tsx +49 -0
  150. package/src/components/icons/components/arrow-right.tsx +52 -0
  151. package/src/components/icons/components/arrow-up.tsx +49 -0
  152. package/src/components/icons/components/chat.tsx +44 -0
  153. package/src/components/icons/components/code.tsx +50 -0
  154. package/src/components/icons/components/copy.tsx +51 -0
  155. package/src/components/icons/components/down.tsx +33 -0
  156. package/src/components/icons/components/home.tsx +57 -0
  157. package/src/components/icons/components/left.tsx +43 -0
  158. package/src/components/icons/components/minus.tsx +42 -0
  159. package/src/components/icons/components/pause-solid.tsx +48 -0
  160. package/src/components/icons/components/pause.tsx +63 -0
  161. package/src/components/icons/components/play-solid.tsx +44 -0
  162. package/src/components/icons/components/play.tsx +51 -0
  163. package/src/components/icons/components/remove.tsx +42 -0
  164. package/src/components/icons/components/resume-solid.tsx +52 -0
  165. package/src/components/icons/components/resume.tsx +57 -0
  166. package/src/components/icons/components/right.tsx +43 -0
  167. package/src/components/icons/components/star.tsx +38 -0
  168. package/src/components/icons/components/stop-solid.tsx +44 -0
  169. package/src/components/icons/components/stop.tsx +54 -0
  170. package/src/components/icons/components/svg.tsx +44 -0
  171. package/src/components/icons/components/up.tsx +31 -0
  172. package/src/components/icons/components/user.tsx +46 -0
  173. package/src/components/icons/icon.scss +15 -0
  174. package/src/components/icons/icon.stories.tsx +208 -0
  175. package/src/components/icons/icon.tsx +100 -0
  176. package/src/components/icons/index.ts +29 -0
  177. package/src/components/icons/types.ts +12 -0
  178. package/src/components/images/README.mdx +43 -0
  179. package/src/components/images/figure.stories.tsx +34 -0
  180. package/src/components/images/figure.tsx +44 -0
  181. package/src/components/images/img.scss +43 -0
  182. package/src/components/images/img.stories.tsx +24 -0
  183. package/src/components/images/img.test.tsx +43 -0
  184. package/src/components/images/img.tsx +93 -0
  185. package/src/components/images/place-holder.png +0 -0
  186. package/src/components/kit.tsx +56 -0
  187. package/src/components/layout/_header.scss +72 -0
  188. package/src/components/layout/footer.stories.tsx +34 -0
  189. package/src/components/layout/landmarks.scss +51 -0
  190. package/src/components/layout/landmarks.stories.tsx +54 -0
  191. package/src/components/layout/landmarks.tsx +149 -0
  192. package/src/components/layout/main.stories.tsx +90 -0
  193. package/src/components/link/link.scss +92 -0
  194. package/src/components/link/link.stories.tsx +74 -0
  195. package/src/components/link/link.tsx +48 -0
  196. package/src/components/list/list.stories.tsx +52 -0
  197. package/src/components/list/list.tsx +74 -0
  198. package/src/components/modal/dialog.tsx +50 -0
  199. package/src/components/modal/modal.tsx +85 -0
  200. package/src/components/nav/nav.scss +90 -0
  201. package/src/components/nav/nav.stories.tsx +96 -0
  202. package/src/components/nav/nav.tsx +76 -0
  203. package/src/components/popover/node_modules/.vitest/results.json +1 -0
  204. package/src/components/popover/popover.stories.tsx +31 -0
  205. package/src/components/popover/popover.test.tsx +39 -0
  206. package/src/components/popover/popover.tsx +85 -0
  207. package/src/components/progress/progress.scss +70 -0
  208. package/src/components/progress/progress.stories.tsx +51 -0
  209. package/src/components/progress/progress.tsx +82 -0
  210. package/src/components/readme.stories.mdx +7 -0
  211. package/src/components/styles/index.css +520 -0
  212. package/src/components/styles/index.css.map +1 -0
  213. package/src/components/tables/table-elements.tsx +57 -0
  214. package/src/components/tables/table.tsx +57 -0
  215. package/src/components/tag/tag.scss +56 -0
  216. package/src/components/tag/tag.stories.tsx +39 -0
  217. package/src/components/tag/tag.tsx +25 -0
  218. package/src/components/text/text.stories.tsx +67 -0
  219. package/src/components/text/text.tsx +93 -0
  220. package/src/components/text-to-speech/README.mdx +192 -0
  221. package/src/components/text-to-speech/TextInput.tsx +19 -0
  222. package/src/components/text-to-speech/TextToSpeech.stories.tsx +145 -0
  223. package/src/components/text-to-speech/TextToSpeech.tsx +94 -0
  224. package/src/components/text-to-speech/text-to-speech.scss +31 -0
  225. package/src/components/text-to-speech/useTextToSpeech.mdx +182 -0
  226. package/src/components/text-to-speech/useTextToSpeech.tsx +176 -0
  227. package/src/components/text-to-speech/views/TextToSpeechControls.tsx +117 -0
  228. package/src/components/ui.tsx +67 -0
  229. package/src/favicon.svg +15 -0
  230. package/src/hooks/popover/__snapshots__/popover.test.tsx.snap +88 -0
  231. package/src/hooks/popover/node_modules/.vitest/results.json +1 -0
  232. package/src/hooks/popover/popover.tsx +71 -0
  233. package/src/hooks/popover/use-popover.tsx +83 -0
  234. package/src/hooks.ts +1 -0
  235. package/src/icons.ts +1 -0
  236. package/src/index.css +13 -0
  237. package/src/index.scss +19 -0
  238. package/src/index.ts +35 -0
  239. package/src/libs/content.ts +30 -0
  240. package/src/logo.svg +7 -0
  241. package/src/main.tsx +10 -0
  242. package/src/patterns/.gitkeep +0 -0
  243. package/src/patterns/page/page-header.stories.tsx +44 -0
  244. package/src/patterns/page/page-header.tsx +78 -0
  245. package/src/sass/_elements.scss +17 -0
  246. package/src/sass/_globals.scss +162 -0
  247. package/src/sass/_layout.scss +51 -0
  248. package/src/sass/_loading-animation.scss +35 -0
  249. package/src/sass/_mixins.scss +10 -0
  250. package/src/sass/_properties.scss +106 -0
  251. package/src/sass/_reset.scss +183 -0
  252. package/src/sass/_type.scss +43 -0
  253. package/src/setupTest.ts +1 -0
  254. package/src/styles/badge/badge.css +22 -0
  255. package/src/styles/badge/badge.css.map +1 -0
  256. package/src/styles/breadcrumbs/breadcrumb.css +42 -0
  257. package/src/styles/breadcrumbs/breadcrumb.css.map +1 -0
  258. package/src/styles/buttons/button.css +93 -0
  259. package/src/styles/buttons/button.css.map +1 -0
  260. package/src/styles/cards/card-style.css +3 -0
  261. package/src/styles/cards/card-style.css.map +1 -0
  262. package/src/styles/cards/card.css +48 -0
  263. package/src/styles/cards/card.css.map +1 -0
  264. package/src/styles/details/details.css +69 -0
  265. package/src/styles/details/details.css.map +1 -0
  266. package/src/styles/dropdowns/dropdown.css.map +1 -0
  267. package/src/styles/form/form.css +93 -0
  268. package/src/styles/form/form.css.map +1 -0
  269. package/src/styles/form/style.css.map +1 -0
  270. package/src/styles/icons/icon.css +16 -0
  271. package/src/styles/icons/icon.css.map +1 -0
  272. package/src/styles/images/img.css +42 -0
  273. package/src/styles/images/img.css.map +1 -0
  274. package/src/styles/index.css +1330 -0
  275. package/src/styles/index.css.map +1 -0
  276. package/src/styles/layout/landmarks.css +155 -0
  277. package/src/styles/layout/landmarks.css.map +1 -0
  278. package/src/styles/link/link.css +88 -0
  279. package/src/styles/link/link.css.map +1 -0
  280. package/src/styles/nav/nav.css +85 -0
  281. package/src/styles/nav/nav.css.map +1 -0
  282. package/src/styles/progress/progress.css +54 -0
  283. package/src/styles/progress/progress.css.map +1 -0
  284. package/src/styles/progress/sass/progress.css.map +1 -0
  285. package/src/styles/styles/index.css +562 -0
  286. package/src/styles/styles/index.css.map +1 -0
  287. package/src/styles/tag/badge.css.map +1 -0
  288. package/src/styles/tag/tag.css +71 -0
  289. package/src/styles/tag/tag.css.map +1 -0
  290. package/src/styles/text-to-speech/text-to-speech.css +32 -0
  291. package/src/styles/text-to-speech/text-to-speech.css.map +1 -0
  292. package/src/test/setup.ts +6 -0
  293. package/src/types/component-props.ts +36 -0
  294. package/src/types/index.ts +2 -0
  295. package/src/types/input-props.ts +28 -0
  296. package/src/types/shared.ts +57 -0
  297. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,39 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ import { within, userEvent, screen } from '@storybook/testing-library'
3
+ import { expect } from '@storybook/jest'
4
+
5
+ import Tag from './tag'
6
+ import './tag.scss'
7
+
8
+ const meta: Meta<typeof Tag> = {
9
+ title: 'FP.React Components/Tag',
10
+ component: Tag,
11
+ args: {
12
+ // @ts-ignore
13
+ children: 'Basic Tag',
14
+ // styles: Tag.styles,
15
+ },
16
+ } as Meta
17
+
18
+ export default meta
19
+ type Story = StoryObj<typeof Tag>
20
+
21
+ export const TagComponent: Story = {
22
+ args: {},
23
+ play: async ({ canvasElement }) => {
24
+ const canvas = within(canvasElement)
25
+ expect(canvas.queryByText(/basic badge/i)).toBeInTheDocument()
26
+ },
27
+ }
28
+
29
+ export const Beta: Story = {
30
+ args: {
31
+ 'data-tag': 'beta',
32
+ },
33
+ } as Story
34
+
35
+ export const Production: Story = {
36
+ args: {
37
+ 'data-tag': 'production',
38
+ },
39
+ } as Story
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import UI from '#components/ui'
3
+
4
+ export type TagProps = {
5
+ /** HTML element to display the badge as span or p */
6
+ elm?: 'span' | 'p'
7
+ /** Aria role for the component - conditional */
8
+ role: 'note' | 'status'
9
+ } & React.ComponentProps<typeof UI>
10
+
11
+ export const Tag = ({
12
+ elm = 'span',
13
+ role = 'note',
14
+ children,
15
+ styles,
16
+ ...props
17
+ }: TagProps) => {
18
+ return (
19
+ <UI as={elm} role={role} styles={styles} {...props}>
20
+ {children}
21
+ </UI>
22
+ )
23
+ }
24
+ export default Tag
25
+ Tag.displayName = 'Tag'
@@ -0,0 +1,67 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ import { within, userEvent, screen } from '@storybook/testing-library'
3
+ import { expect } from '@storybook/jest'
4
+
5
+ import Text from './text'
6
+
7
+ const meta: Meta<typeof Text> = {
8
+ title: 'FP.REACT Components/Text',
9
+ component: Text,
10
+ args: {
11
+ // @ts-ignore
12
+ children:
13
+ 'Exercitation non voluptate fugiat amet dolor tempor consectetur. Eu esse adipisicing laboris duis et velit in quis et sunt pariatur tempor laborum nisi. Et id amet ullamco culpa irure nulla esse dolore velit esse.',
14
+ },
15
+ } as Story
16
+
17
+ export default meta
18
+ type Story = StoryObj<typeof Text>
19
+
20
+ export const ParagraphText: Story = {
21
+ args: {},
22
+ play: async ({ canvasElement }) => {
23
+ const canvas = within(canvasElement)
24
+ expect(canvas.getByText(/link/i)).toBeInTheDocument()
25
+ },
26
+ }
27
+
28
+ export const span: Story = {
29
+ args: {
30
+ as: 'span',
31
+ children: 'Heading Text',
32
+ },
33
+ play: async ({ canvasElement }) => {
34
+ const canvas = within(canvasElement)
35
+ expect(canvas.getByText(/heading/i)).toBeInTheDocument()
36
+ },
37
+ } as Story
38
+
39
+ export const Blockquote: Story = {
40
+ args: {
41
+ as: 'blockquote',
42
+ children:
43
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.',
44
+ },
45
+ } as Story
46
+
47
+ export const Strong: Story = {
48
+ args: {
49
+ as: 'strong',
50
+ children: 'Emphasis Text',
51
+ },
52
+ play: async ({ canvasElement }) => {
53
+ const canvas = within(canvasElement)
54
+ expect(canvas.getByText(/emphasis/i)).toBeInTheDocument()
55
+ },
56
+ } as Story
57
+
58
+ export const Code: Story = {
59
+ args: {
60
+ as: 'code',
61
+ children: 'Code Text',
62
+ },
63
+ play: async ({ canvasElement }) => {
64
+ const canvas = within(canvasElement)
65
+ expect(canvas.getByText(/code/i)).toBeInTheDocument()
66
+ },
67
+ } as Story
@@ -0,0 +1,93 @@
1
+ // import FP from '../fp'
2
+ import React from 'react'
3
+ import UI from '#components/ui'
4
+
5
+ type InheritedProps = React.ComponentProps<typeof UI>
6
+
7
+ type TextElements =
8
+ | 'a'
9
+ | 'b'
10
+ | 'blockquote'
11
+ | 'b'
12
+ | 'blockquote'
13
+ | 'cite'
14
+ | 'code'
15
+ | 'em'
16
+ | 'i'
17
+ | 'em'
18
+ | 'i'
19
+ | 'kbd'
20
+ | 'mark'
21
+ | 'p'
22
+ | 's'
23
+ | 'small'
24
+ | 'span'
25
+ | 'span'
26
+ | 'strong'
27
+ | 'mark'
28
+ | 'p'
29
+ | 's'
30
+ | 'small'
31
+ | 'span'
32
+ | 'span'
33
+ | 'strong'
34
+ | 'sub'
35
+ | 'sup'
36
+ | 'time'
37
+ | 'time'
38
+ | 'u'
39
+
40
+ export type TextProps = {
41
+ /**
42
+ * Text element to to use
43
+ * Text element to to use
44
+ */
45
+ elm?: TextElements
46
+ /** Pass a text element or string */
47
+ text?: string
48
+ } & InheritedProps
49
+
50
+ export const Text = ({
51
+ elm = 'p',
52
+ id,
53
+ text,
54
+ styles,
55
+ classes,
56
+ children,
57
+ ...props
58
+ }: TextProps) => {
59
+ return (
60
+ <UI as={elm} id={id} styles={styles} className={classes} {...props}>
61
+ <UI as={elm} id={id} styles={styles} className={classes} {...props}>
62
+ {children || text}
63
+ </UI>
64
+ </UI>
65
+ )
66
+ }
67
+
68
+ type TitleProps = {
69
+ /**
70
+ * HTML headings
71
+ */
72
+ elm?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
73
+ } & InheritedProps
74
+
75
+ export const Title = ({
76
+ elm = 'h3',
77
+ id,
78
+ children,
79
+ styles,
80
+ classes,
81
+ ...props
82
+ }: TitleProps) => {
83
+ return (
84
+ <Text as={elm} id={id} styles={styles} className={classes} {...props}>
85
+ {children}
86
+ </Text>
87
+ )
88
+ }
89
+
90
+ export default Text
91
+
92
+ Text.displayName = 'Text'
93
+ Title.displayName = 'Title'
@@ -0,0 +1,192 @@
1
+ # Text-to-Speech Components
2
+
3
+ This folder contains components and hooks related to text-to-speech functionality in the FPKit React library.
4
+
5
+ ## Contents
6
+
7
+ 1. `useTextToSpeech.tsx`: A custom React hook that provides text-to-speech functionality.
8
+ 2. `TextToSpeech.tsx`: A React component that implements the text-to-speech functionality using the `useTextToSpeech` hook.
9
+ 3. `TextToSpeech.stories.tsx`: Storybook stories for the TextToSpeech component.
10
+ 4. `useTextToSpeech.mdx`: Documentation for the `useTextToSpeech` hook.
11
+
12
+ ## Usage
13
+
14
+ ### useTextToSpeech Hook
15
+
16
+ The `useTextToSpeech` hook provides a simple way to add text-to-speech capabilities to your React components.
17
+
18
+ ```tsx
19
+ import { useTextToSpeech } from '@fpkit/react';
20
+
21
+ function MyComponent() {
22
+ const { speak, pause, resume, cancel } = useTextToSpeech();
23
+
24
+ const handleSpeak = () => {
25
+ speak('Hello, world!');
26
+ };
27
+
28
+ return (
29
+ <button onClick={handleSpeak}>
30
+ Speak
31
+ </button>
32
+ );
33
+ }
34
+ ```
35
+
36
+
37
+ ## Example 2
38
+
39
+ Here's a detailed example of how to use the `useTextToSpeech` hook in a React component:
40
+
41
+ ```tsx
42
+ import React, { useState, useEffect } from 'react';
43
+ import { useTextToSpeech } from '@fpkit/react';
44
+
45
+ const TextToSpeechExample: React.FC = () => {
46
+ const [text, setText] = useState('Welcome to the text-to-speech example!');
47
+ const [rate, setRate] = useState(1);
48
+ const [pitch, setPitch] = useState(1);
49
+ const [volume, setVolume] = useState(1);
50
+
51
+ const {
52
+ speak,
53
+ pause,
54
+ resume,
55
+ cancel,
56
+ isSpeaking,
57
+ isPaused,
58
+ error
59
+ } = useTextToSpeech({ rate, pitch, volume });
60
+
61
+ useEffect(() => {
62
+ if (error) {
63
+ console.error('Text-to-speech error:', error);
64
+ }
65
+ }, [error]);
66
+
67
+ const handleSpeak = () => {
68
+ speak(text);
69
+ };
70
+
71
+ return (
72
+ <div>
73
+ <h2>Text-to-Speech Example</h2>
74
+ <textarea
75
+ value={text}
76
+ onChange={(e) => setText(e.target.value)}
77
+ rows={4}
78
+ cols={50}
79
+ />
80
+ <div>
81
+ <label>
82
+ Rate:
83
+ <input
84
+ type="range"
85
+ min="0.1"
86
+ max="10"
87
+ step="0.1"
88
+ value={rate}
89
+ onChange={(e) => setRate(parseFloat(e.target.value))}
90
+ />
91
+ {rate}
92
+ </label>
93
+ </div>
94
+ <div>
95
+ <label>
96
+ Pitch:
97
+ <input
98
+ type="range"
99
+ min="0"
100
+ max="2"
101
+ step="0.1"
102
+ value={pitch}
103
+ onChange={(e) => setPitch(parseFloat(e.target.value))}
104
+ />
105
+ {pitch}
106
+ </label>
107
+ </div>
108
+ <div>
109
+ <label>
110
+ Volume:
111
+ <input
112
+ type="range"
113
+ min="0"
114
+ max="1"
115
+ step="0.1"
116
+ value={volume}
117
+ onChange={(e) => setVolume(parseFloat(e.target.value))}
118
+ />
119
+ {volume}
120
+ </label>
121
+ </div>
122
+ <div>
123
+ <button onClick={handleSpeak} disabled={isSpeaking && !isPaused}>
124
+ Speak
125
+ </button>
126
+ <button onClick={pause} disabled={!isSpeaking || isPaused}>
127
+ Pause
128
+ </button>
129
+ <button onClick={resume} disabled={!isPaused}>
130
+ Resume
131
+ </button>
132
+ <button onClick={cancel} disabled={!isSpeaking && !isPaused}>
133
+ Cancel
134
+ </button>
135
+ </div>
136
+ <div>
137
+ Status: {isSpeaking ? (isPaused ? 'Paused' : 'Speaking') : 'Idle'}
138
+ </div>
139
+ {error && <div style={{ color: 'red' }}>Error: {error.message}</div>}
140
+ </div>
141
+ );
142
+ };
143
+
144
+ export default TextToSpeechExample;
145
+ ```
146
+
147
+ This example demonstrates:
148
+
149
+ 1. Using all the functions and state variables returned by the hook.
150
+ 2. Allowing users to input custom text.
151
+ 3. Providing controls for rate, pitch, and volume.
152
+ 4. Displaying the current speaking status.
153
+ 5. Handling and displaying errors.
154
+ 6. Disabling buttons based on the current state.
155
+
156
+
157
+
158
+ For more detailed information on the `useTextToSpeech` hook, refer to the `useTextToSpeech.mdx` file.
159
+
160
+ ### TextToSpeech Component
161
+
162
+ The `TextToSpeech` component provides a ready-to-use implementation of text-to-speech functionality.
163
+
164
+ ```tsx
165
+ import { TextToSpeech } from '@fpkit/react';
166
+
167
+ function MyComponent() {
168
+ return (
169
+ <TextToSpeech text="Hello, world!" />
170
+ );
171
+ }
172
+ ```
173
+
174
+ ## Documentation
175
+
176
+ For comprehensive documentation on the `useTextToSpeech` hook, including API details and usage examples, please refer to the `useTextToSpeech.mdx` file in this folder.
177
+
178
+ ## Storybook
179
+
180
+ The `TextToSpeech.stories.tsx` file contains Storybook stories that demonstrate the usage and various configurations of the TextToSpeech component. You can run Storybook to interact with these components in isolation and see how they behave with different props.
181
+
182
+ ## Contributing
183
+
184
+ When contributing to this folder, please ensure that:
185
+
186
+ 1. Any new functionality is properly documented in the respective MDX files.
187
+ 2. Storybook stories are updated or added for new features or components.
188
+ 3. The README is kept up-to-date with any significant changes.
189
+
190
+ ## Testing
191
+
192
+ Ensure that all components and hooks in this folder have appropriate unit tests. Run the test suite before submitting any pull requests.
@@ -0,0 +1,19 @@
1
+ import React, { ChangeEvent } from 'react';
2
+
3
+ interface TextInputProps {
4
+ value: string;
5
+ onChange: (e: ChangeEvent<HTMLTextAreaElement>) => void;
6
+ }
7
+
8
+ const TextInput: React.FC<TextInputProps> = ({ value, onChange }) => {
9
+ return (
10
+ <textarea
11
+ value={value}
12
+ onChange={onChange}
13
+ placeholder="Enter text to speak"
14
+ style={{ width: '100%', height: '100px', marginBottom: '10px' }}
15
+ />
16
+ );
17
+ };
18
+
19
+ export default TextInput;
@@ -0,0 +1,145 @@
1
+ import React from 'react'
2
+ import { Meta, StoryObj } from '@storybook/react'
3
+ import TextToSpeech from './TextToSpeech'
4
+ import './text-to-speech.scss'
5
+
6
+ const meta: Meta<typeof TextToSpeech> = {
7
+ title: 'FP.REACT Components/TextToSpeech',
8
+ component: TextToSpeech,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ voice: {
12
+ control: {
13
+ type: 'select',
14
+ options: [
15
+ 'Google US English',
16
+ 'Google UK English Female',
17
+ 'Google UK English Male',
18
+ ],
19
+ },
20
+ },
21
+ },
22
+ } as Story
23
+
24
+ export default meta
25
+
26
+ type Story = StoryObj<typeof TextToSpeech>
27
+
28
+ export const Default: Story = {
29
+ args: {
30
+ label: 'Read post',
31
+ initialText:
32
+ 'Hello, how are you? I am a Text to Speech (TTS) assistant! Update this text and click the [speak] button to hear the change.',
33
+ },
34
+ parameters: {
35
+ docs: {
36
+ description: {
37
+ story:
38
+ "This story demonstrates the TextToSpeechComponent with predefined text and hidden text input. It's useful for scenarios where you want to display the component with a fixed text, without allowing the user to modify it.",
39
+ },
40
+ },
41
+ },
42
+ } as Story
43
+
44
+ export const WithText: Story = {
45
+ args: {
46
+ // Add props to simulate text input
47
+ initialText:
48
+ 'This is a test of the text to speech component. Here the test is passed as a prop and the text input is hidden.',
49
+ showTextInput: false,
50
+ },
51
+ parameters: {
52
+ docs: {
53
+ description: {
54
+ story:
55
+ "This story demonstrates the TextToSpeechComponent with predefined text and hidden text input. It's useful for scenarios where you want to display the component with a fixed text, without allowing the user to modify it.",
56
+ },
57
+ },
58
+ },
59
+ } as Story
60
+
61
+ export const FullFeature: Story = {
62
+ args: {
63
+ initialText:
64
+ 'Welcome to the full-featured Text-to-Speech component. You can modify this text, choose a voice, adjust pitch and rate, and then click the speak button to hear it.',
65
+ showTextInput: true,
66
+ pitch: 1,
67
+ rate: 1,
68
+ },
69
+ parameters: {
70
+ docs: {
71
+ description: {
72
+ story:
73
+ "This story showcases all features of the TextToSpeechComponent, including text input, voice selection, pitch and rate adjustment. It provides a comprehensive demonstration of the component's capabilities.",
74
+ },
75
+ },
76
+ },
77
+ render: (args) => {
78
+ const [selectedVoice, setSelectedVoice] = React.useState<
79
+ SpeechSynthesisVoice | undefined
80
+ >(undefined)
81
+ const [pitch, setPitch] = React.useState(1)
82
+ const [rate, setRate] = React.useState(1)
83
+
84
+ React.useEffect(() => {
85
+ const voices = window.speechSynthesis.getVoices()
86
+ setSelectedVoice(
87
+ voices.find((voice) => voice.name === 'Google US English') || voices[0],
88
+ )
89
+ }, [])
90
+
91
+ return (
92
+ <div>
93
+ <TextToSpeech
94
+ {...args}
95
+ voice={selectedVoice}
96
+ pitch={pitch}
97
+ rate={rate}
98
+ />
99
+ <div style={{ marginTop: '20px' }}>
100
+ <label htmlFor="voice-select">Select Voice: </label>
101
+ <select
102
+ id="voice-select"
103
+ onChange={(e) =>
104
+ setSelectedVoice(
105
+ window.speechSynthesis
106
+ .getVoices()
107
+ .find((v) => v.name === e.target.value),
108
+ )
109
+ }
110
+ >
111
+ {window.speechSynthesis.getVoices().map((voice) => (
112
+ <option key={voice.name} value={voice.name}>
113
+ {voice.name} ({voice.lang})
114
+ </option>
115
+ ))}
116
+ </select>
117
+ </div>
118
+ <div style={{ marginTop: '10px' }}>
119
+ <label htmlFor="pitch-range">Pitch: {pitch}</label>
120
+ <input
121
+ id="pitch-range"
122
+ type="range"
123
+ min="0.5"
124
+ max="2"
125
+ step="0.1"
126
+ value={pitch}
127
+ onChange={(e) => setPitch(parseFloat(e.target.value))}
128
+ />
129
+ </div>
130
+ <div style={{ marginTop: '10px' }}>
131
+ <label htmlFor="rate-range">Rate: {rate}</label>
132
+ <input
133
+ id="rate-range"
134
+ type="range"
135
+ min="0.5"
136
+ max="2"
137
+ step="0.1"
138
+ value={rate}
139
+ onChange={(e) => setRate(parseFloat(e.target.value))}
140
+ />
141
+ </div>
142
+ </div>
143
+ )
144
+ },
145
+ } as Story
@@ -0,0 +1,94 @@
1
+ import React, { useState, ChangeEvent, useEffect } from 'react'
2
+ import { useTextToSpeech } from './useTextToSpeech'
3
+ import Textarea from '#components/form/textarea.jsx'
4
+ import TextToSpeechControls from './views/TextToSpeechControls'
5
+
6
+ /**
7
+ * Props for the TextToSpeechComponent.
8
+ * @interface TextToSpeechComponentProps
9
+ */
10
+ interface TextToSpeechComponentProps {
11
+ /** Initial text to be spoken. Defaults to an empty string. */
12
+ initialText?: string
13
+ /** Whether to show the text input field. Defaults to true. */
14
+ showTextInput?: boolean
15
+ /** The voice to be used for speech synthesis. */
16
+ voice?: SpeechSynthesisVoice | undefined
17
+ /** The pitch of the voice. Defaults to 1. */
18
+ pitch?: number
19
+ /** The rate of speech. Defaults to 1. */
20
+ rate?: number
21
+ /** The language to be used for speech synthesis. */
22
+ language?: string
23
+ /** Player label */
24
+ label?: string | React.ReactNode
25
+ /** Callback function to be called when speech ends. */
26
+ onEnd?: () => void
27
+ }
28
+
29
+ /**
30
+ * A component that converts text to speech using the Web Speech API.
31
+ * @param {TextToSpeechComponentProps} props - The props for the component.
32
+ * @returns {JSX.Element} The rendered TextToSpeechComponent.
33
+ */
34
+ export const TextToSpeech: React.FC<TextToSpeechComponentProps> = ({
35
+ initialText = '',
36
+ showTextInput = false,
37
+ voice,
38
+ pitch = 1,
39
+ rate = 1,
40
+ language,
41
+ label,
42
+ onEnd,
43
+ }) => {
44
+ const {
45
+ speak,
46
+ pause,
47
+ resume,
48
+ cancel,
49
+ isSpeaking,
50
+ isPaused,
51
+ getAvailableLanguages,
52
+ availableVoices,
53
+ } = useTextToSpeech()
54
+ const [text, setText] = useState<string>(initialText)
55
+ console.log(getAvailableLanguages())
56
+
57
+ useEffect(() => {
58
+ setText(initialText)
59
+ }, [initialText])
60
+
61
+ const handleSpeak = (): void => {
62
+ if (text.trim() !== '') {
63
+ speak(text, { voice, pitch, rate }, handleEnd)
64
+ }
65
+ }
66
+
67
+ const handleChange = (e: ChangeEvent<HTMLTextAreaElement>): void => {
68
+ setText(e.target.value)
69
+ }
70
+
71
+ const handleEnd = (): void => {
72
+ if (onEnd) {
73
+ onEnd()
74
+ }
75
+ }
76
+
77
+ return (
78
+ <>
79
+ {showTextInput && <Textarea value={text} onChange={handleChange} />}
80
+ <TextToSpeechControls
81
+ label={label}
82
+ isSpeaking={isSpeaking}
83
+ isPaused={isPaused}
84
+ onSpeak={handleSpeak}
85
+ onPause={pause}
86
+ onResume={resume}
87
+ onCancel={cancel}
88
+ />
89
+ </>
90
+ )
91
+ }
92
+
93
+ export default TextToSpeech
94
+ TextToSpeech.displayName = 'TextToSpeechComponent'
@@ -0,0 +1,31 @@
1
+ [data-tts] {
2
+ --tts-gap: 0.5rem;
3
+ --tts-bg: #fff;
4
+ --tts-border-width: 0.125rem;
5
+ --tts-border-style: solid;
6
+ --tts-border-color: currentColor;
7
+ --tts-radius: 99rem;
8
+ --tts-padding: 0.5rem;
9
+ --tts-min-width: 20.3125rem;
10
+ --tts-align-items: center;
11
+ --tts-justify-content: center;
12
+
13
+ display: flex;
14
+ gap: var(--tts-gap);
15
+ align-items: var(--tts-align-items);
16
+ justify-content: var(--tts-justify-content);
17
+ background-color: var(--tts-bg);
18
+ border: none;
19
+ outline: var(--tts-border-width) var(--tts-border-style)
20
+ var(--tts-border-color);
21
+ border-radius: var(--tts-radius);
22
+ padding: var(--tts-padding);
23
+ min-width: var(--tts-min-width);
24
+ button[data-btn~='tts-btn'],
25
+ .tts-border {
26
+ outline: none;
27
+ &:hover {
28
+ outline: var(--tts-border-width) var(--tts-border-style);
29
+ }
30
+ }
31
+ }