@sakura-ui/sakura-ui 0.2.0 → 0.4.1

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 (212) hide show
  1. package/README.md +77 -6
  2. package/package.json +28 -21
  3. package/packages/core/package.json +30 -17
  4. package/packages/core/src/components/Button.tsx +33 -32
  5. package/packages/core/src/components/Card.tsx +37 -20
  6. package/packages/core/src/components/Code.tsx +6 -4
  7. package/packages/core/src/components/Faq.tsx +13 -7
  8. package/packages/core/src/components/Heading.tsx +42 -32
  9. package/packages/core/src/components/Icon.tsx +43 -13
  10. package/packages/core/src/components/IconButton.tsx +54 -43
  11. package/packages/core/src/components/LangSelector.tsx +135 -0
  12. package/packages/core/src/components/Link.tsx +37 -66
  13. package/packages/core/src/components/LinkCard.tsx +126 -0
  14. package/packages/core/src/components/List.tsx +14 -12
  15. package/packages/core/src/components/MenuButton.tsx +112 -0
  16. package/packages/core/src/components/NavigationItem.tsx +41 -0
  17. package/packages/core/src/components/NotificationBanner.tsx +20 -0
  18. package/packages/core/src/components/OverflowContainer.tsx +24 -0
  19. package/packages/core/src/components/PopoverMenu.tsx +82 -0
  20. package/packages/core/src/components/Pre.tsx +6 -4
  21. package/packages/core/src/components/Table.tsx +31 -25
  22. package/packages/core/src/components/buttonStyle.ts +43 -32
  23. package/packages/core/src/components/index.ts +13 -25
  24. package/packages/core/src/icons/Close.tsx +21 -0
  25. package/packages/core/src/icons/CloseMobile.tsx +43 -0
  26. package/packages/core/src/icons/KeyboardArrowDown.tsx +24 -0
  27. package/packages/core/src/icons/Language.tsx +25 -0
  28. package/packages/core/src/icons/LanguageMobile.tsx +40 -0
  29. package/packages/core/src/icons/Menu.tsx +24 -0
  30. package/packages/core/src/icons/MenuMobile.tsx +40 -0
  31. package/packages/core/src/icons/index.ts +8 -0
  32. package/packages/core/src/icons/props.ts +5 -0
  33. package/packages/core/src/index.ts +10 -28
  34. package/packages/core/src/type.d.ts +1 -0
  35. package/packages/core/tests/Button.test.tsx +20 -10
  36. package/packages/core/tests/Card.test.tsx +16 -11
  37. package/packages/core/tests/IconButton.test.tsx +57 -0
  38. package/packages/core/tests/Link.test.tsx +60 -0
  39. package/packages/core/tests/vitest.setup.ts +1 -0
  40. package/packages/core/tsconfig.node.json +3 -2
  41. package/packages/core/vite.config.ts +2 -1
  42. package/packages/forms/.turbo/turbo-build.log +12 -0
  43. package/packages/forms/dist/index.cjs.js +183 -131
  44. package/packages/forms/dist/index.es.js +750 -1062
  45. package/packages/forms/dist/types/components/Checkbox.d.ts +9 -4
  46. package/packages/forms/dist/types/components/Checkbox.d.ts.map +1 -1
  47. package/packages/forms/dist/types/components/FieldsetControl.d.ts +14 -9
  48. package/packages/forms/dist/types/components/FieldsetControl.d.ts.map +1 -1
  49. package/packages/forms/dist/types/components/FileInput.d.ts +10 -3
  50. package/packages/forms/dist/types/components/FileInput.d.ts.map +1 -1
  51. package/packages/forms/dist/types/components/Input.d.ts +9 -4
  52. package/packages/forms/dist/types/components/Input.d.ts.map +1 -1
  53. package/packages/forms/dist/types/components/LabelControl.d.ts +13 -8
  54. package/packages/forms/dist/types/components/LabelControl.d.ts.map +1 -1
  55. package/packages/forms/dist/types/components/Radio.d.ts +9 -4
  56. package/packages/forms/dist/types/components/Radio.d.ts.map +1 -1
  57. package/packages/forms/dist/types/components/Select.d.ts +9 -4
  58. package/packages/forms/dist/types/components/Select.d.ts.map +1 -1
  59. package/packages/forms/dist/types/components/Textarea.d.ts +8 -3
  60. package/packages/forms/dist/types/components/Textarea.d.ts.map +1 -1
  61. package/packages/forms/dist/types/components/controlled/CheckboxGroup.d.ts +16 -11
  62. package/packages/forms/dist/types/components/controlled/CheckboxGroup.d.ts.map +1 -1
  63. package/packages/forms/dist/types/components/controlled/RadioGroup.d.ts +17 -12
  64. package/packages/forms/dist/types/components/controlled/RadioGroup.d.ts.map +1 -1
  65. package/packages/forms/dist/types/components/index.d.ts +8 -8
  66. package/packages/forms/dist/types/components/index.d.ts.map +1 -1
  67. package/packages/forms/dist/types/components/inputStyle.d.ts +1 -1
  68. package/packages/forms/dist/types/components/inputStyle.d.ts.map +1 -1
  69. package/packages/forms/dist/types/index.d.ts +0 -1
  70. package/packages/forms/dist/types/index.d.ts.map +1 -1
  71. package/packages/forms/node_modules/.bin/autoprefixer +44 -6
  72. package/packages/forms/package.json +21 -16
  73. package/packages/forms/src/components/Checkbox.tsx +76 -80
  74. package/packages/forms/src/components/FieldsetControl.tsx +21 -28
  75. package/packages/forms/src/components/FileInput.tsx +92 -63
  76. package/packages/forms/src/components/Input.tsx +29 -31
  77. package/packages/forms/src/components/LabelControl.tsx +19 -25
  78. package/packages/forms/src/components/Radio.tsx +68 -69
  79. package/packages/forms/src/components/Select.tsx +65 -65
  80. package/packages/forms/src/components/Textarea.tsx +49 -54
  81. package/packages/forms/src/components/controlled/CheckboxGroup.tsx +15 -17
  82. package/packages/forms/src/components/controlled/RadioGroup.tsx +16 -18
  83. package/packages/forms/src/components/index.ts +8 -8
  84. package/packages/forms/src/components/inputStyle.ts +7 -8
  85. package/packages/forms/src/index.ts +0 -11
  86. package/packages/forms/tests/Checkbox.test.tsx +44 -0
  87. package/packages/forms/tests/FieldsetControl.test.tsx +68 -11
  88. package/packages/forms/tests/FileInput.test.tsx +25 -0
  89. package/packages/forms/tests/Input.test.tsx +12 -83
  90. package/packages/forms/tests/LabelControl.test.tsx +88 -20
  91. package/packages/forms/tests/Radio.test.tsx +23 -78
  92. package/packages/forms/tests/Select.test.tsx +30 -0
  93. package/packages/forms/tests/Textarea.test.tsx +25 -0
  94. package/packages/forms/tests/vitest.setup.ts +1 -0
  95. package/packages/forms/tsconfig.node.json +3 -2
  96. package/packages/forms/vite.config.ts +2 -1
  97. package/packages/helper/.turbo/turbo-build.log +12 -0
  98. package/packages/helper/dist/index.cjs.js +76 -0
  99. package/packages/helper/dist/index.es.js +119 -0
  100. package/packages/helper/dist/types/calc.d.ts +2 -0
  101. package/packages/helper/dist/types/calc.d.ts.map +1 -0
  102. package/packages/{forms/dist/types/utils/class.d.ts → helper/dist/types/cx.d.ts} +1 -1
  103. package/packages/helper/dist/types/cx.d.ts.map +1 -0
  104. package/packages/helper/dist/types/index.d.ts +6 -0
  105. package/packages/helper/dist/types/index.d.ts.map +1 -0
  106. package/packages/helper/dist/types/querySelector.d.ts +2 -0
  107. package/packages/helper/dist/types/querySelector.d.ts.map +1 -0
  108. package/packages/helper/dist/types/styles.d.ts +22 -0
  109. package/packages/helper/dist/types/styles.d.ts.map +1 -0
  110. package/packages/helper/dist/types/treefy.d.ts +8 -0
  111. package/packages/helper/dist/types/treefy.d.ts.map +1 -0
  112. package/packages/helper/node_modules/.bin/vitest +55 -0
  113. package/packages/helper/package.json +50 -0
  114. package/packages/helper/src/calc.ts +1 -0
  115. package/packages/helper/src/cx.ts +2 -0
  116. package/packages/helper/src/index.ts +5 -0
  117. package/packages/helper/src/querySelector.ts +20 -0
  118. package/packages/helper/src/styles.ts +114 -0
  119. package/packages/helper/src/treefy.ts +24 -0
  120. package/packages/helper/tests/calc.test.ts +22 -0
  121. package/packages/helper/tests/cx.test.ts +30 -0
  122. package/packages/helper/tests/querySelector.test.ts +24 -0
  123. package/packages/helper/tests/treefy.test.ts +111 -0
  124. package/packages/helper/tests/vitest.setup.ts +2 -0
  125. package/packages/helper/tsconfig.json +13 -0
  126. package/packages/helper/tsconfig.node.json +6 -0
  127. package/packages/helper/vite.config.ts +26 -0
  128. package/packages/markdown/.turbo/turbo-build.log +12 -0
  129. package/packages/markdown/README.md +129 -0
  130. package/packages/markdown/dist/index.cjs.js +149 -0
  131. package/packages/markdown/dist/index.es.js +16960 -0
  132. package/packages/markdown/dist/types/components/Markdown.d.ts +15 -0
  133. package/packages/markdown/dist/types/components/Markdown.d.ts.map +1 -0
  134. package/packages/markdown/dist/types/components/index.d.ts +2 -0
  135. package/packages/markdown/dist/types/components/index.d.ts.map +1 -0
  136. package/packages/markdown/dist/types/index.d.ts +2 -0
  137. package/packages/markdown/dist/types/index.d.ts.map +1 -0
  138. package/packages/markdown/dist/types/plugins/attr.d.ts +3 -0
  139. package/packages/markdown/dist/types/plugins/attr.d.ts.map +1 -0
  140. package/packages/markdown/dist/types/plugins/card.d.ts +3 -0
  141. package/packages/markdown/dist/types/plugins/card.d.ts.map +1 -0
  142. package/packages/markdown/dist/types/plugins/cell.d.ts +3 -0
  143. package/packages/markdown/dist/types/plugins/cell.d.ts.map +1 -0
  144. package/packages/markdown/dist/types/plugins/faq.d.ts +3 -0
  145. package/packages/markdown/dist/types/plugins/faq.d.ts.map +1 -0
  146. package/packages/markdown/dist/types/plugins/grid.d.ts +3 -0
  147. package/packages/markdown/dist/types/plugins/grid.d.ts.map +1 -0
  148. package/packages/markdown/dist/types/plugins/headings.d.ts +11 -0
  149. package/packages/markdown/dist/types/plugins/headings.d.ts.map +1 -0
  150. package/packages/markdown/dist/types/plugins/helper.d.ts +9 -0
  151. package/packages/markdown/dist/types/plugins/helper.d.ts.map +1 -0
  152. package/packages/markdown/dist/types/plugins/index.d.ts +9 -0
  153. package/packages/markdown/dist/types/plugins/index.d.ts.map +1 -0
  154. package/packages/markdown/dist/types/plugins/linkButton.d.ts +3 -0
  155. package/packages/markdown/dist/types/plugins/linkButton.d.ts.map +1 -0
  156. package/packages/markdown/dist/types/plugins/youtube.d.ts +3 -0
  157. package/packages/markdown/dist/types/plugins/youtube.d.ts.map +1 -0
  158. package/packages/markdown/node_modules/.bin/autoprefixer +55 -0
  159. package/packages/markdown/package.json +84 -0
  160. package/packages/markdown/src/components/Markdown.tsx +365 -0
  161. package/packages/markdown/src/components/index.ts +1 -0
  162. package/packages/markdown/src/global.d.ts +9 -0
  163. package/packages/markdown/src/index.ts +1 -0
  164. package/packages/markdown/src/plugins/attr.ts +19 -0
  165. package/packages/markdown/src/plugins/card.ts +57 -0
  166. package/packages/markdown/src/plugins/cell.ts +47 -0
  167. package/packages/markdown/src/plugins/faq.ts +40 -0
  168. package/packages/markdown/src/plugins/grid.ts +54 -0
  169. package/packages/markdown/src/plugins/headings.ts +38 -0
  170. package/packages/markdown/src/plugins/helper.ts +27 -0
  171. package/packages/markdown/src/plugins/index.ts +8 -0
  172. package/packages/markdown/src/plugins/linkButton.ts +26 -0
  173. package/packages/markdown/src/plugins/youtube.ts +49 -0
  174. package/packages/markdown/tsconfig.json +13 -0
  175. package/packages/markdown/tsconfig.node.json +7 -0
  176. package/packages/markdown/vite.config.ts +30 -0
  177. package/packages/tailwind-theme-plugin/#package.json# +51 -0
  178. package/packages/tailwind-theme-plugin/.turbo/turbo-build.log +12 -0
  179. package/packages/tailwind-theme-plugin/dist/index.cjs.js +1 -1
  180. package/packages/tailwind-theme-plugin/dist/index.es.js +4970 -6093
  181. package/packages/tailwind-theme-plugin/dist/types/index.d.ts +1 -4
  182. package/packages/tailwind-theme-plugin/dist/types/index.d.ts.map +1 -1
  183. package/packages/tailwind-theme-plugin/node_modules/.bin/autoprefixer +44 -6
  184. package/packages/tailwind-theme-plugin/package.json +11 -9
  185. package/packages/tailwind-theme-plugin/src/index.ts +241 -349
  186. package/packages/tailwind-theme-plugin/vite.config.ts +2 -0
  187. package/packages/core/src/libs/cx.ts +0 -2
  188. package/packages/core/src/libs/forward-ref.ts +0 -44
  189. package/packages/core/src/types/component.ts +0 -83
  190. package/packages/forms/dist/types/utils/class.d.ts.map +0 -1
  191. package/packages/forms/node_modules/.bin/acorn +0 -17
  192. package/packages/forms/node_modules/.bin/browserslist +0 -17
  193. package/packages/forms/node_modules/.bin/eslint +0 -17
  194. package/packages/forms/node_modules/.bin/tailwind +0 -17
  195. package/packages/forms/node_modules/.bin/tailwindcss +0 -17
  196. package/packages/forms/node_modules/.bin/ts-node +0 -17
  197. package/packages/forms/node_modules/.bin/ts-node-cwd +0 -17
  198. package/packages/forms/node_modules/.bin/ts-node-esm +0 -17
  199. package/packages/forms/node_modules/.bin/ts-node-script +0 -17
  200. package/packages/forms/node_modules/.bin/ts-node-transpile-only +0 -17
  201. package/packages/forms/node_modules/.bin/ts-script +0 -17
  202. package/packages/forms/node_modules/.bin/vitest +0 -17
  203. package/packages/forms/src/utils/class.ts +0 -2
  204. package/packages/tailwind-theme-plugin/node_modules/.bin/browserslist +0 -17
  205. package/packages/tailwind-theme-plugin/node_modules/.bin/tailwind +0 -17
  206. package/packages/tailwind-theme-plugin/node_modules/.bin/tailwindcss +0 -17
  207. package/packages/tailwind-theme-plugin/node_modules/.bin/ts-node +0 -17
  208. package/packages/tailwind-theme-plugin/node_modules/.bin/ts-node-cwd +0 -17
  209. package/packages/tailwind-theme-plugin/node_modules/.bin/ts-node-esm +0 -17
  210. package/packages/tailwind-theme-plugin/node_modules/.bin/ts-node-script +0 -17
  211. package/packages/tailwind-theme-plugin/node_modules/.bin/ts-node-transpile-only +0 -17
  212. package/packages/tailwind-theme-plugin/node_modules/.bin/ts-script +0 -17
@@ -1,18 +1,18 @@
1
1
  import React from 'react'
2
- import { cx } from '../libs/cx'
2
+ import { cx } from '@sakura-ui/helper'
3
3
 
4
- export interface HeadingProps extends React.ComponentPropsWithoutRef<'h1'> {}
4
+ export namespace H1 {
5
+ export interface Props extends React.ComponentPropsWithoutRef<'h1'> {}
6
+ }
5
7
 
6
- export const H1 = (props: HeadingProps) => {
8
+ export const H1 = (props: H1.Props) => {
7
9
  const { className, children, ...restProps } = props
8
10
 
9
11
  const style = `
10
12
  text-h-lg-m
11
13
  sm:text-h-lg
12
- mt-8
13
- mb-3
14
- sm:mt-16
15
- sm:mb-6
14
+ pt-[2rem]
15
+ sm:pt-[6.5rem]
16
16
  `
17
17
 
18
18
  return (
@@ -22,16 +22,18 @@ export const H1 = (props: HeadingProps) => {
22
22
  )
23
23
  }
24
24
 
25
- export const H2 = (props: HeadingProps) => {
25
+ export namespace H2 {
26
+ export interface Props extends React.ComponentPropsWithoutRef<'h2'> {}
27
+ }
28
+
29
+ export const H2 = (props: H2.Props) => {
26
30
  const { className, children, ...restProps } = props
27
31
 
28
32
  const style = `
29
33
  text-h-med-m
30
34
  sm:text-h-med
31
- mt-8
32
- mb-3
33
- sm:mt-16
34
- sm:mb-6
35
+ pt-[4rem]
36
+ sm:pt-[4.5rem]
35
37
  `
36
38
 
37
39
  return (
@@ -41,16 +43,18 @@ export const H2 = (props: HeadingProps) => {
41
43
  )
42
44
  }
43
45
 
44
- export const H3 = (props: HeadingProps) => {
46
+ export namespace H3 {
47
+ export interface Props extends React.ComponentPropsWithoutRef<'h3'> {}
48
+ }
49
+
50
+ export const H3 = (props: H3.Props) => {
45
51
  const { className, children, ...restProps } = props
46
52
 
47
53
  const style = `
48
54
  text-h-sm-m
49
55
  sm:text-h-sm
50
- mt-5
51
- mb-3
52
- sm:mt-10
53
- sm:mb-6
56
+ pt-[4rem]
57
+ sm:pt-[4.5rem]
54
58
  `
55
59
 
56
60
  return (
@@ -60,16 +64,18 @@ export const H3 = (props: HeadingProps) => {
60
64
  )
61
65
  }
62
66
 
63
- export const H4 = (props: HeadingProps) => {
67
+ export namespace H4 {
68
+ export interface Props extends React.ComponentPropsWithoutRef<'h4'> {}
69
+ }
70
+
71
+ export const H4 = (props: H4.Props) => {
64
72
  const { className, children, ...restProps } = props
65
73
 
66
74
  const style = `
67
75
  text-h-xs-m
68
76
  sm:text-h-xs
69
- mt-5
70
- mb-2
71
- sm:mt-10
72
- sm:mb-4
77
+ pt-[3.5rem]
78
+ sm:pt-[4.5rem]
73
79
  `
74
80
 
75
81
  return (
@@ -79,16 +85,18 @@ export const H4 = (props: HeadingProps) => {
79
85
  )
80
86
  }
81
87
 
82
- export const H5 = (props: HeadingProps) => {
88
+ export namespace H5 {
89
+ export interface Props extends React.ComponentPropsWithoutRef<'h5'> {}
90
+ }
91
+
92
+ export const H5 = (props: H5.Props) => {
83
93
  const { className, children, ...restProps } = props
84
94
 
85
95
  const style = `
86
96
  text-h-xxs-m
87
97
  sm:text-h-xxs
88
- mt-5
89
- mb-2
90
- sm:mt-10
91
- sm:mb-4
98
+ pt-[3rem]
99
+ sm:pt-[4rem]
92
100
  `
93
101
 
94
102
  return (
@@ -98,16 +106,18 @@ export const H5 = (props: HeadingProps) => {
98
106
  )
99
107
  }
100
108
 
101
- export const H6 = (props: HeadingProps) => {
109
+ export namespace H6 {
110
+ export interface Props extends React.ComponentPropsWithoutRef<'h6'> {}
111
+ }
112
+
113
+ export const H6 = (props: H6.Props) => {
102
114
  const { className, children, ...restProps } = props
103
115
 
104
116
  const style = `
105
117
  text-h-xxs-m
106
118
  sm:text-h-xxs
107
- mt-3
108
- mb-2
109
- sm:mt-6
110
- sm:mb-4
119
+ pt-[2.5rem]
120
+ sm:pt-[3.5rem]
111
121
  `
112
122
 
113
123
  return (
@@ -1,32 +1,62 @@
1
1
  import React from 'react'
2
- import { cx } from '../libs/cx'
2
+ import { cx } from '@sakura-ui/helper'
3
3
 
4
- export interface IconProps extends React.ComponentPropsWithRef<'span'> {
5
- icon: string
6
- srText?: string
4
+ export namespace Icon {
5
+ export interface Props extends React.ComponentPropsWithRef<'span'> {
6
+ opticalSize?: 16 | 20 | 24 | 40 | 44 | 48
7
+ altText?: string
8
+ }
7
9
  }
8
10
 
9
- export const Icon = React.forwardRef<HTMLElement, IconProps>((props, ref) => {
10
- const { className, icon, srText, ...restProps } = props
11
+ export const Icon = React.forwardRef<HTMLElement, Icon.Props>((props, ref) => {
12
+ const { className, altText = '', opticalSize = 24, children, ...rest } = props
13
+
14
+ const size = {
15
+ 16: '!text-[16px]',
16
+ 20: '!text-[20px]',
17
+ 24: '!text-[24px]',
18
+ 40: '!text-[40px]',
19
+ 44: '!text-[44px]',
20
+ 48: '!text-[48px]'
21
+ }
22
+
23
+ const fontSize = size[opticalSize]
24
+
25
+ // @see: https://developers.google.com/fonts/docs/material_symbols
26
+ //.material-symbols-outlined {
27
+ // font-family: 'Material Symbols Outlined';
28
+ // font-weight: normal;
29
+ // font-style: normal;
30
+ // font-size: 24px; /* Preferred icon size */
31
+ // display: inline-block;
32
+ // line-height: 1;
33
+ // text-transform: none;
34
+ // letter-spacing: normal;
35
+ // word-wrap: normal;
36
+ // white-space: nowrap;
37
+ // direction: ltr;
38
+ //}
11
39
 
12
40
  const style = `
13
- inline
14
- align-middle
15
41
  font-icon
16
- antialiased
42
+ font-light
43
+ inline-block
44
+ leading-none
45
+ align-middle
46
+ whitespace-nowrap
17
47
  `
18
48
 
19
49
  return (
20
50
  <>
21
51
  <span
22
52
  aria-hidden="true"
23
- className={cx(style, className)}
24
- {...restProps}
53
+ className={cx(className, fontSize, style)}
54
+ {...rest}
25
55
  ref={ref}
26
56
  >
27
- {icon}
57
+ {children}
28
58
  </span>
29
- <span className="sr-only">{srText}</span>
59
+ <span className="sr-only">{altText}</span>
30
60
  </>
31
61
  )
32
62
  })
@@ -1,33 +1,43 @@
1
- import { cx } from '../libs/cx'
2
- import type { ComponentWithAs } from '../types/component'
3
- import { forwardRef } from '../libs/forward-ref'
4
- import { base, getVariantStyle, getSizeStyle } from './buttonStyle'
1
+ import React from 'react'
2
+ import { cx } from '@sakura-ui/helper'
3
+ import {
4
+ base,
5
+ getVariantStyle,
6
+ getSizeStyle,
7
+ type ButtonVariant,
8
+ type ButtonSize
9
+ } from './buttonStyle'
5
10
 
6
- export interface IconButtonProps {
7
- variant?: 'primary' | 'secondary'
8
- size?: 'lg' | 'md' | 'sm' | 'xs'
9
- iconLayout?: 'left' | 'right'
10
- icon: string
11
- rounded?: string
11
+ export namespace IconButton {
12
+ export interface Props<T extends React.ElementType> {
13
+ as?: T
14
+ variant?: ButtonVariant
15
+ size?: ButtonSize
16
+ iconLayout?: 'left' | 'right'
17
+ icon: string
18
+ rounded?: string
19
+ }
12
20
  }
13
21
 
14
- export const IconButton: ComponentWithAs<'button', IconButtonProps> =
15
- forwardRef((props, ref) => {
16
- const {
17
- as: Component = 'button',
18
- variant = 'primary',
19
- size = 'lg',
20
- iconLayout = 'left',
21
- icon,
22
- rounded,
23
- className,
24
- children,
25
- ...restProps
26
- } = props
22
+ export const IconButton = <T extends React.ElementType = 'button'>(
23
+ props: IconButton.Props<T> &
24
+ Omit<React.ComponentProps<T>, keyof IconButton.Props<T>>
25
+ ) => {
26
+ const {
27
+ as: Component = 'button',
28
+ variant = 'solid-fill',
29
+ size = 'lg',
30
+ iconLayout = 'left',
31
+ icon,
32
+ rounded,
33
+ className,
34
+ children,
35
+ ...restProps
36
+ } = props
27
37
 
28
- const styleFontSize = `${size === 'xs' ? 'scale-105' : 'scale-125'}`
38
+ const styleFontSize = `${size === 'xs' ? 'scale-105' : 'scale-125'}`
29
39
 
30
- const styleIcon = `
40
+ const styleIcon = `
31
41
  inline-block
32
42
  align-middle
33
43
  mb-1
@@ -37,23 +47,23 @@ export const IconButton: ComponentWithAs<'button', IconButtonProps> =
37
47
  antialiased
38
48
  `
39
49
 
40
- const styleRounded = `${rounded ? 'aspect-square !rounded-full !py-0' : ''}`
50
+ const styleRounded = `${rounded ? 'aspect-square !rounded-full !py-0' : ''}`
41
51
 
42
- // When text is specified on a button, set the 'aria-hidden' attribute of the icon to true.
43
- const ariaHidden = !!children
52
+ // When text is specified on a button, set the 'aria-hidden' attribute of the icon to true.
53
+ const ariaHidden = !!children
44
54
 
45
- return (
46
- <Component
47
- className={cx(
48
- base,
49
- getVariantStyle(variant),
50
- getSizeStyle(size),
51
- styleRounded,
52
- className
53
- )}
54
- {...restProps}
55
- ref={ref}
56
- >
55
+ return (
56
+ <Component
57
+ className={cx(
58
+ base,
59
+ getVariantStyle(variant),
60
+ getSizeStyle(size),
61
+ styleRounded,
62
+ className
63
+ )}
64
+ {...restProps}
65
+ >
66
+ <span>
57
67
  {iconLayout === 'left' && (
58
68
  <span
59
69
  className={cx(styleIcon, styleFontSize)}
@@ -71,8 +81,9 @@ export const IconButton: ComponentWithAs<'button', IconButtonProps> =
71
81
  {icon}
72
82
  </span>
73
83
  )}
74
- </Component>
75
- )
76
- })
84
+ </span>
85
+ </Component>
86
+ )
87
+ }
77
88
 
78
89
  IconButton.displayName = 'IconButton'
@@ -0,0 +1,135 @@
1
+ import React from 'react'
2
+ import { cx, mod, focusableSelector, Style } from '@sakura-ui/helper'
3
+ import { Language, LanguageMobile, KeyboadArrowDown } from '@/icons'
4
+ import { PopoverMenu, PopoverMenuItem } from './PopoverMenu'
5
+
6
+ export namespace LangSelector {
7
+ interface Lang {
8
+ code: string
9
+ title: string
10
+ path: string
11
+ }
12
+ // Tailwind 3.4.5 or higher is required to use anchor-name.
13
+ // https://github.com/tailwindlabs/tailwindcss/issues/13818
14
+ export interface Props extends React.ComponentPropsWithRef<'button'> {
15
+ current: string
16
+ langs: Lang[]
17
+ }
18
+ }
19
+
20
+ export const LangSelector = ({
21
+ current,
22
+ langs,
23
+ className,
24
+ ...restProps
25
+ }: LangSelector.Props) => {
26
+ const styleRoot = `
27
+ relative
28
+ group
29
+ `
30
+
31
+ const styleAnchorName = `
32
+ [anchor-name:--target]
33
+ `
34
+
35
+ // height: text-base(16px)*1.375 + p-3(12px)*2 + border(1px)*2 = 48px
36
+ const styleButton = `
37
+ w-fit
38
+ py-[1px]
39
+ sm:py-3
40
+ text-base
41
+ leading-snug
42
+ ${Style.clickable}
43
+ ${Style.focusRoundedWithBg}
44
+ ${Style.hoverUnderline}
45
+ ${styleAnchorName}
46
+ flex items-center
47
+ `
48
+
49
+ const stylePositionBottomRight = `
50
+ absolute
51
+ top-[anchor(--target_bottom)]
52
+ right-[anchor(--target_right)]
53
+ position-try-options:flip-block]
54
+ inset-[unset]
55
+ `
56
+
57
+ const targetId = React.useId()
58
+
59
+ const menuRef = React.useRef<HTMLUListElement>(null)
60
+
61
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
62
+ if (e.key === 'Tab') {
63
+ e.preventDefault()
64
+
65
+ const focusables: HTMLElement[] = Array.from(
66
+ menuRef.current?.querySelectorAll(focusableSelector) || []
67
+ )
68
+ if (focusables.length === 0) return
69
+
70
+ const tabIndex = focusables.indexOf(
71
+ document.activeElement as HTMLElement
72
+ )
73
+ // If there is no focused element, focus the first element
74
+ if (tabIndex === -1) {
75
+ focusables[0].focus()
76
+ return
77
+ }
78
+ const nextIndex = mod(tabIndex + (e.shiftKey ? -1 : 1), focusables.length)
79
+ focusables[nextIndex].focus()
80
+
81
+ return
82
+ }
83
+ }
84
+
85
+ React.useEffect(() => {
86
+ const handleToggle = () => {
87
+ const focusable: HTMLElement | null | undefined =
88
+ menuRef.current?.querySelector(focusableSelector)
89
+ if (!focusable) return
90
+ focusable.focus()
91
+ }
92
+
93
+ menuRef.current?.addEventListener('toggle', handleToggle)
94
+
95
+ return () => menuRef.current?.removeEventListener('toggle', handleToggle)
96
+ }, [])
97
+
98
+ return (
99
+ <div className={cx(className, styleRoot)}>
100
+ <button
101
+ popoverTarget={targetId}
102
+ type="button"
103
+ className={styleButton}
104
+ {...restProps}
105
+ >
106
+ <Language size={20} className="hidden sm:mt-0.5 sm:inline-block" />
107
+ <span className="hidden sm:mx-1 sm:inline">Language</span>
108
+ <LanguageMobile size={44} className="inline-block sm:hidden" />
109
+ <KeyboadArrowDown
110
+ size={16}
111
+ className="inline-block sm:mt-1 group-[:has(:popover-open)]:rotate-180"
112
+ />
113
+ </button>
114
+ <PopoverMenu
115
+ ref={menuRef}
116
+ id={targetId}
117
+ popover="auto"
118
+ onKeyDown={handleKeyDown}
119
+ className={stylePositionBottomRight}
120
+ >
121
+ {langs.map((lang) => (
122
+ <PopoverMenuItem
123
+ key={lang.code}
124
+ aria-current={current === lang.code}
125
+ href={lang.path}
126
+ >
127
+ {lang.title}
128
+ </PopoverMenuItem>
129
+ ))}
130
+ </PopoverMenu>
131
+ </div>
132
+ )
133
+ }
134
+
135
+ LangSelector.displayName = 'LangSelector'
@@ -1,79 +1,50 @@
1
- import { cx } from '../libs/cx'
2
- import type { ComponentWithAs } from '../types/component'
3
- import { forwardRef } from '../libs/forward-ref'
1
+ import React from 'react'
2
+ import { cx, Style } from '@sakura-ui/helper'
3
+ import { Icon } from './Icon'
4
4
 
5
- // biome-ignore lint/suspicious/noEmptyInterface:
6
- export interface LinkProps {}
7
-
8
- export const Link: ComponentWithAs<'a', LinkProps> = forwardRef(
9
- (props, ref) => {
10
- const {
11
- as: Component = 'a',
12
- href,
13
- className,
14
- children,
15
- ...restProps
16
- } = props
17
-
18
- const styleUnderLine = `
19
- underline
20
- underline-offset-[0.125em]
21
- `
5
+ export namespace Link {
6
+ export interface Props<T extends React.ElementType> {
7
+ as?: T
8
+ }
9
+ }
22
10
 
23
- // Make lines break at each word
24
- const style = `
25
- rounded-sm
26
- cursor-pointer
27
- text-sea-600
28
- hover:text-sea-800
29
- active:text-sea-800
30
- visited:text-sea-900
31
- focus:outline
32
- focus:outline-2
33
- focus:outline-wood-500
34
- disabled:border-sumi-500
35
- [overflow-wrap:anywhere]
36
- `
11
+ export const Link = <T extends React.ElementType = 'a'>(
12
+ props: Link.Props<T> & Omit<React.ComponentProps<T>, keyof Link.Props<T>>
13
+ ) => {
14
+ const { as: Component = 'a', className, children, ...restProps } = props
37
15
 
38
- const styleIcon = `
39
- align-middle
40
- ml-0.5
41
- text-sumi-700
42
- font-icon
43
- font-light
44
- leading-4
45
- antialiased
46
- `
16
+ // Make lines break at each word
17
+ const style = `
18
+ cursor-pointer
19
+ text-blue-1000
20
+ active:text-orange-700
21
+ visited:text-magenta-900
22
+ ${Style.focusRoundedWithBg}
23
+ ${Style.underline}
24
+ disabled:border-solid-gray-500
25
+ [overflow-wrap:anywhere]
26
+ `
47
27
 
48
- if (href?.startsWith('http')) {
49
- return (
50
- <Component
51
- className={cx(style, className)}
52
- href={href}
53
- target="_blank"
54
- rel="noopener noreferrer"
55
- {...restProps}
56
- ref={ref}
57
- >
58
- <span className={styleUnderLine}>{children}</span>
59
- <span className={cx(styleIcon)} aria-hidden="true">
60
- open_in_new
61
- </span>
62
- <span className="sr-only">Opens in new tab</span>
63
- </Component>
64
- )
65
- }
28
+ if (restProps.href?.startsWith('http') || restProps.target === '_blank') {
66
29
  return (
67
30
  <Component
68
- className={cx(style, styleUnderLine, className)}
69
- href={href}
31
+ className={cx(style, className)}
32
+ target="_blank"
70
33
  {...restProps}
71
- ref={ref}
72
34
  >
73
- {children}
35
+ <span>{children}</span>
36
+ <Icon opticalSize={16} altText="Opens in new tab" className="ml-0.5">
37
+ open_in_new
38
+ </Icon>
74
39
  </Component>
75
40
  )
76
41
  }
77
- )
42
+
43
+ return (
44
+ <Component className={cx(style, className)} {...restProps}>
45
+ {children}
46
+ </Component>
47
+ )
48
+ }
78
49
 
79
50
  Link.displayName = 'Link'