jvetrau-ds 0.1.44 → 0.2.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 (176) hide show
  1. package/components/button/button.cjs +41 -16
  2. package/components/button/button.cjs.map +1 -1
  3. package/components/button/button.css +80 -7
  4. package/components/button/button.d.ts +26 -3
  5. package/components/button/button.js +41 -16
  6. package/components/button/button.js.map +1 -1
  7. package/components/flip-list/flip-list.cjs +39 -16
  8. package/components/flip-list/flip-list.cjs.map +1 -1
  9. package/components/flip-list/flip-list.css +49 -3
  10. package/components/flip-list/flip-list.d.ts +31 -6
  11. package/components/flip-list/flip-list.js +39 -16
  12. package/components/flip-list/flip-list.js.map +1 -1
  13. package/components/index.cjs +0 -6
  14. package/components/index.d.ts +0 -2
  15. package/components/index.js +0 -1
  16. package/components/tabs/tabs.cjs +25 -9
  17. package/components/tabs/tabs.cjs.map +1 -1
  18. package/components/tabs/tabs.css +63 -0
  19. package/components/tabs/tabs.d.ts +1 -0
  20. package/components/tabs/tabs.js +26 -10
  21. package/components/tabs/tabs.js.map +1 -1
  22. package/components/video/video.cjs +150 -60
  23. package/components/video/video.cjs.map +1 -1
  24. package/components/video/video.js +147 -58
  25. package/components/video/video.js.map +1 -1
  26. package/index.cjs +0 -6
  27. package/index.d.ts +0 -2
  28. package/index.js +0 -1
  29. package/package.json +1 -1
  30. package/styles.css +178 -65
  31. package/themes/base/desktop.css +1 -0
  32. package/themes/base/desktop.md +1 -0
  33. package/themes/base/desktop.scoped.css +1 -0
  34. package/themes/base/diagram.css +1 -0
  35. package/themes/base/diagram.md +1 -0
  36. package/themes/base/diagram.scoped.css +1 -0
  37. package/themes/base/mobile.css +1 -0
  38. package/themes/base/mobile.md +1 -0
  39. package/themes/base/mobile.scoped.css +1 -0
  40. package/themes/brandcopilot/desktop.css +1 -0
  41. package/themes/brandcopilot/desktop.md +1 -0
  42. package/themes/brandcopilot/desktop.scoped.css +1 -0
  43. package/themes/brandcopilot/diagram.css +1 -0
  44. package/themes/brandcopilot/diagram.md +1 -0
  45. package/themes/brandcopilot/diagram.scoped.css +1 -0
  46. package/themes/brandcopilot/mobile.css +1 -0
  47. package/themes/brandcopilot/mobile.md +1 -0
  48. package/themes/brandcopilot/mobile.scoped.css +1 -0
  49. package/themes/dmpatterns/desktop.css +1 -0
  50. package/themes/dmpatterns/desktop.md +1 -0
  51. package/themes/dmpatterns/desktop.scoped.css +1 -0
  52. package/themes/dmpatterns/diagram.css +1 -0
  53. package/themes/dmpatterns/diagram.md +1 -0
  54. package/themes/dmpatterns/diagram.scoped.css +1 -0
  55. package/themes/dmpatterns/mobile.css +1 -0
  56. package/themes/dmpatterns/mobile.md +1 -0
  57. package/themes/dmpatterns/mobile.scoped.css +1 -0
  58. package/themes/pdigest/desktop.css +1 -0
  59. package/themes/pdigest/desktop.md +1 -0
  60. package/themes/pdigest/desktop.scoped.css +1 -0
  61. package/themes/pdigest/diagram.css +1 -0
  62. package/themes/pdigest/diagram.md +1 -0
  63. package/themes/pdigest/diagram.scoped.css +1 -0
  64. package/themes/pdigest/mobile.css +1 -0
  65. package/themes/pdigest/mobile.md +1 -0
  66. package/themes/pdigest/mobile.scoped.css +1 -0
  67. package/themes/redesigntrack/desktop.css +1 -0
  68. package/themes/redesigntrack/desktop.md +1 -0
  69. package/themes/redesigntrack/desktop.scoped.css +1 -0
  70. package/themes/redesigntrack/diagram.css +1 -0
  71. package/themes/redesigntrack/diagram.md +1 -0
  72. package/themes/redesigntrack/diagram.scoped.css +1 -0
  73. package/themes/redesigntrack/mobile.css +1 -0
  74. package/themes/redesigntrack/mobile.md +1 -0
  75. package/themes/redesigntrack/mobile.scoped.css +1 -0
  76. package/tokens/runtime/tokens.base.desktop.runtime.cjs +1 -0
  77. package/tokens/runtime/tokens.base.desktop.runtime.cjs.map +1 -1
  78. package/tokens/runtime/tokens.base.desktop.runtime.d.ts +1 -0
  79. package/tokens/runtime/tokens.base.desktop.runtime.js +1 -0
  80. package/tokens/runtime/tokens.base.desktop.runtime.js.map +1 -1
  81. package/tokens/runtime/tokens.base.diagram.runtime.cjs +1 -0
  82. package/tokens/runtime/tokens.base.diagram.runtime.cjs.map +1 -1
  83. package/tokens/runtime/tokens.base.diagram.runtime.d.ts +1 -0
  84. package/tokens/runtime/tokens.base.diagram.runtime.js +1 -0
  85. package/tokens/runtime/tokens.base.diagram.runtime.js.map +1 -1
  86. package/tokens/runtime/tokens.base.mobile.runtime.cjs +1 -0
  87. package/tokens/runtime/tokens.base.mobile.runtime.cjs.map +1 -1
  88. package/tokens/runtime/tokens.base.mobile.runtime.d.ts +1 -0
  89. package/tokens/runtime/tokens.base.mobile.runtime.js +1 -0
  90. package/tokens/runtime/tokens.base.mobile.runtime.js.map +1 -1
  91. package/tokens/runtime/tokens.brandcopilot.desktop.runtime.cjs +1 -0
  92. package/tokens/runtime/tokens.brandcopilot.desktop.runtime.cjs.map +1 -1
  93. package/tokens/runtime/tokens.brandcopilot.desktop.runtime.d.ts +1 -0
  94. package/tokens/runtime/tokens.brandcopilot.desktop.runtime.js +1 -0
  95. package/tokens/runtime/tokens.brandcopilot.desktop.runtime.js.map +1 -1
  96. package/tokens/runtime/tokens.brandcopilot.diagram.runtime.cjs +1 -0
  97. package/tokens/runtime/tokens.brandcopilot.diagram.runtime.cjs.map +1 -1
  98. package/tokens/runtime/tokens.brandcopilot.diagram.runtime.d.ts +1 -0
  99. package/tokens/runtime/tokens.brandcopilot.diagram.runtime.js +1 -0
  100. package/tokens/runtime/tokens.brandcopilot.diagram.runtime.js.map +1 -1
  101. package/tokens/runtime/tokens.brandcopilot.mobile.runtime.cjs +1 -0
  102. package/tokens/runtime/tokens.brandcopilot.mobile.runtime.cjs.map +1 -1
  103. package/tokens/runtime/tokens.brandcopilot.mobile.runtime.d.ts +1 -0
  104. package/tokens/runtime/tokens.brandcopilot.mobile.runtime.js +1 -0
  105. package/tokens/runtime/tokens.brandcopilot.mobile.runtime.js.map +1 -1
  106. package/tokens/runtime/tokens.dmpatterns.desktop.runtime.cjs +1 -0
  107. package/tokens/runtime/tokens.dmpatterns.desktop.runtime.cjs.map +1 -1
  108. package/tokens/runtime/tokens.dmpatterns.desktop.runtime.d.ts +1 -0
  109. package/tokens/runtime/tokens.dmpatterns.desktop.runtime.js +1 -0
  110. package/tokens/runtime/tokens.dmpatterns.desktop.runtime.js.map +1 -1
  111. package/tokens/runtime/tokens.dmpatterns.diagram.runtime.cjs +1 -0
  112. package/tokens/runtime/tokens.dmpatterns.diagram.runtime.cjs.map +1 -1
  113. package/tokens/runtime/tokens.dmpatterns.diagram.runtime.d.ts +1 -0
  114. package/tokens/runtime/tokens.dmpatterns.diagram.runtime.js +1 -0
  115. package/tokens/runtime/tokens.dmpatterns.diagram.runtime.js.map +1 -1
  116. package/tokens/runtime/tokens.dmpatterns.mobile.runtime.cjs +1 -0
  117. package/tokens/runtime/tokens.dmpatterns.mobile.runtime.cjs.map +1 -1
  118. package/tokens/runtime/tokens.dmpatterns.mobile.runtime.d.ts +1 -0
  119. package/tokens/runtime/tokens.dmpatterns.mobile.runtime.js +1 -0
  120. package/tokens/runtime/tokens.dmpatterns.mobile.runtime.js.map +1 -1
  121. package/tokens/runtime/tokens.pdigest.desktop.runtime.cjs +1 -0
  122. package/tokens/runtime/tokens.pdigest.desktop.runtime.cjs.map +1 -1
  123. package/tokens/runtime/tokens.pdigest.desktop.runtime.d.ts +1 -0
  124. package/tokens/runtime/tokens.pdigest.desktop.runtime.js +1 -0
  125. package/tokens/runtime/tokens.pdigest.desktop.runtime.js.map +1 -1
  126. package/tokens/runtime/tokens.pdigest.diagram.runtime.cjs +1 -0
  127. package/tokens/runtime/tokens.pdigest.diagram.runtime.cjs.map +1 -1
  128. package/tokens/runtime/tokens.pdigest.diagram.runtime.d.ts +1 -0
  129. package/tokens/runtime/tokens.pdigest.diagram.runtime.js +1 -0
  130. package/tokens/runtime/tokens.pdigest.diagram.runtime.js.map +1 -1
  131. package/tokens/runtime/tokens.pdigest.mobile.runtime.cjs +1 -0
  132. package/tokens/runtime/tokens.pdigest.mobile.runtime.cjs.map +1 -1
  133. package/tokens/runtime/tokens.pdigest.mobile.runtime.d.ts +1 -0
  134. package/tokens/runtime/tokens.pdigest.mobile.runtime.js +1 -0
  135. package/tokens/runtime/tokens.pdigest.mobile.runtime.js.map +1 -1
  136. package/tokens/runtime/tokens.redesigntrack.desktop.runtime.cjs +1 -0
  137. package/tokens/runtime/tokens.redesigntrack.desktop.runtime.cjs.map +1 -1
  138. package/tokens/runtime/tokens.redesigntrack.desktop.runtime.d.ts +1 -0
  139. package/tokens/runtime/tokens.redesigntrack.desktop.runtime.js +1 -0
  140. package/tokens/runtime/tokens.redesigntrack.desktop.runtime.js.map +1 -1
  141. package/tokens/runtime/tokens.redesigntrack.diagram.runtime.cjs +1 -0
  142. package/tokens/runtime/tokens.redesigntrack.diagram.runtime.cjs.map +1 -1
  143. package/tokens/runtime/tokens.redesigntrack.diagram.runtime.d.ts +1 -0
  144. package/tokens/runtime/tokens.redesigntrack.diagram.runtime.js +1 -0
  145. package/tokens/runtime/tokens.redesigntrack.diagram.runtime.js.map +1 -1
  146. package/tokens/runtime/tokens.redesigntrack.mobile.runtime.cjs +1 -0
  147. package/tokens/runtime/tokens.redesigntrack.mobile.runtime.cjs.map +1 -1
  148. package/tokens/runtime/tokens.redesigntrack.mobile.runtime.d.ts +1 -0
  149. package/tokens/runtime/tokens.redesigntrack.mobile.runtime.js +1 -0
  150. package/tokens/runtime/tokens.redesigntrack.mobile.runtime.js.map +1 -1
  151. package/tokens/themes/base/dist/desktop.json +8 -0
  152. package/tokens/themes/base/dist/diagram.json +8 -0
  153. package/tokens/themes/base/dist/mobile.json +8 -0
  154. package/tokens/themes/brandcopilot/dist/desktop.json +8 -0
  155. package/tokens/themes/brandcopilot/dist/diagram.json +8 -0
  156. package/tokens/themes/brandcopilot/dist/mobile.json +8 -0
  157. package/tokens/themes/dmpatterns/dist/desktop.json +8 -0
  158. package/tokens/themes/dmpatterns/dist/diagram.json +8 -0
  159. package/tokens/themes/dmpatterns/dist/mobile.json +8 -0
  160. package/tokens/themes/pdigest/dist/desktop.json +8 -0
  161. package/tokens/themes/pdigest/dist/diagram.json +8 -0
  162. package/tokens/themes/pdigest/dist/mobile.json +8 -0
  163. package/tokens/themes/redesigntrack/dist/desktop.json +8 -0
  164. package/tokens/themes/redesigntrack/dist/diagram.json +8 -0
  165. package/tokens/themes/redesigntrack/dist/mobile.json +8 -0
  166. package/components/button-icon/button-icon.cjs +0 -48
  167. package/components/button-icon/button-icon.cjs.map +0 -1
  168. package/components/button-icon/button-icon.css +0 -67
  169. package/components/button-icon/button-icon.d.ts +0 -10
  170. package/components/button-icon/button-icon.js +0 -42
  171. package/components/button-icon/button-icon.js.map +0 -1
  172. package/components/button-icon/index.cjs +0 -13
  173. package/components/button-icon/index.cjs.map +0 -1
  174. package/components/button-icon/index.d.ts +0 -2
  175. package/components/button-icon/index.js +0 -3
  176. package/components/button-icon/index.js.map +0 -1
@@ -3,43 +3,68 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
  require('./button.css');
6
- var classnames = require('../helpers/classnames');
7
6
  var Icon = require('../../icons/icon');
7
+ var classnames = require('../helpers/classnames');
8
8
 
9
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
10
 
11
11
  var Icon__default = /*#__PURE__*/_interopDefault(Icon);
12
12
 
13
+ function hasContent(value) {
14
+ return value !== null && value !== void 0;
15
+ }
13
16
  const Button = ({
14
- state = "default",
17
+ mode = "default",
18
+ rank = "default",
19
+ density = "default",
20
+ loading = false,
15
21
  icon,
16
22
  className = "",
17
23
  disabled = false,
18
24
  children,
19
25
  ...props
20
26
  }) => {
21
- const isIconOnly = !!icon && !children;
22
- const classes = classnames.classnames("button", className, {
23
- "button--primary": state === "primary",
24
- "button--disabled": disabled,
25
- "button--icon-only": isIconOnly
26
- });
27
+ const hasIcon = hasContent(icon);
28
+ const hasChildren = hasContent(children);
29
+ const isIconOnly = hasIcon && !hasChildren;
30
+ const isDisabled = disabled || loading;
31
+ const classes = classnames.classnames(
32
+ "button",
33
+ className,
34
+ {
35
+ "button--primary": rank === "primary",
36
+ "button--link": rank === "link",
37
+ "button--compact": density === "compact",
38
+ "button--toolbar": mode === "toolbar",
39
+ "button--disabled": isDisabled,
40
+ "button--loading": loading,
41
+ "button--icon-only": isIconOnly
42
+ }
43
+ );
27
44
  let resolvedIcon = icon;
28
45
  if (react.isValidElement(icon) && icon.type === Icon__default.default) {
29
46
  const iconElement = icon;
30
- resolvedIcon = react.cloneElement(iconElement, {
31
- size: iconElement.props.size ?? "base",
32
- className: classnames.classnames("button__icon", iconElement.props.className),
33
- "aria-hidden": iconElement.props["aria-hidden"] ?? true,
34
- focusable: iconElement.props.focusable ?? false
35
- });
47
+ resolvedIcon = react.cloneElement(
48
+ iconElement,
49
+ {
50
+ size: iconElement.props.size ?? "base",
51
+ className: classnames.classnames(
52
+ "button__icon",
53
+ iconElement.props.className
54
+ ),
55
+ "aria-hidden": iconElement.props["aria-hidden"] ?? true,
56
+ focusable: iconElement.props.focusable ?? false
57
+ }
58
+ );
36
59
  }
60
+ const resolvedAriaBusy = loading ? true : props["aria-busy"];
37
61
  return /* @__PURE__ */ jsxRuntime.jsxs(
38
62
  "button",
39
63
  {
40
- className: classes,
41
- disabled,
42
64
  ...props,
65
+ className: classes,
66
+ disabled: isDisabled,
67
+ "aria-busy": resolvedAriaBusy,
43
68
  children: [
44
69
  resolvedIcon,
45
70
  children
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/button/button.tsx"],"names":["classnames","isValidElement","Icon","cloneElement","jsxs"],"mappings":";;;;;;;;;;;;AAeA,MAAM,SAAgC,CAAC;AAAA,EACrC,KAAA,GAAQ,SAAA;AAAA,EACR,IAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,UAAA,GAAa,CAAC,CAAC,IAAA,IAAQ,CAAC,QAAA;AAE9B,EAAA,MAAM,OAAA,GAAUA,qBAAA,CAAW,QAAA,EAAU,SAAA,EAAW;AAAA,IAC9C,mBAAmB,KAAA,KAAU,SAAA;AAAA,IAC7B,kBAAA,EAAoB,QAAA;AAAA,IACpB,mBAAA,EAAqB;AAAA,GACtB,CAAA;AAED,EAAA,IAAI,YAAA,GAAe,IAAA;AAEnB,EAAA,IAAIC,oBAAA,CAAe,IAAI,CAAA,IAAK,IAAA,CAAK,SAASC,qBAAA,EAAM;AAC9C,IAAA,MAAM,WAAA,GAAc,IAAA;AAEpB,IAAA,YAAA,GAAeC,mBAAa,WAAA,EAAa;AAAA,MACvC,IAAA,EAAM,WAAA,CAAY,KAAA,CAAM,IAAA,IAAQ,MAAA;AAAA,MAChC,SAAA,EAAWH,qBAAA,CAAW,cAAA,EAAgB,WAAA,CAAY,MAAM,SAAS,CAAA;AAAA,MACjE,aAAA,EAAe,WAAA,CAAY,KAAA,CAAM,aAAa,CAAA,IAAK,IAAA;AAAA,MACnD,SAAA,EAAW,WAAA,CAAY,KAAA,CAAM,SAAA,IAAa;AAAA,KAC3C,CAAA;AAAA,EACH;AAEA,EAAA,uBACEI,eAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,OAAA;AAAA,MACX,QAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,YAAA;AAAA,QACA;AAAA;AAAA;AAAA,GACH;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"button.cjs","sourcesContent":["import React, {\n cloneElement,\n isValidElement,\n type ReactElement,\n} from 'react';\nimport './button.css';\nimport { classnames } from '../helpers/classnames';\nimport Icon, { type IconProps } from '../../icons/icon';\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n state?: 'default' | 'primary';\n icon?: React.ReactNode;\n}\n\nconst Button: React.FC<ButtonProps> = ({\n state = 'default',\n icon,\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const isIconOnly = !!icon && !children;\n\n const classes = classnames('button', className, {\n 'button--primary': state === 'primary',\n 'button--disabled': disabled,\n 'button--icon-only': isIconOnly,\n });\n\n let resolvedIcon = icon;\n\n if (isValidElement(icon) && icon.type === Icon) {\n const iconElement = icon as ReactElement<IconProps>;\n\n resolvedIcon = cloneElement(iconElement, {\n size: iconElement.props.size ?? 'base',\n className: classnames('button__icon', iconElement.props.className),\n 'aria-hidden': iconElement.props['aria-hidden'] ?? true,\n focusable: iconElement.props.focusable ?? false,\n });\n }\n\n return (\n <button\n className={classes}\n disabled={disabled}\n {...props}\n >\n {resolvedIcon}\n {children}\n </button>\n );\n};\n\nexport default Button;"]}
1
+ {"version":3,"sources":["../../../src/components/button/button.tsx"],"names":["classnames","isValidElement","Icon","cloneElement","jsxs"],"mappings":";;;;;;;;;;;;AA6DA,SAAS,WACP,KAAA,EAIA;AACA,EAAA,OAAO,KAAA,KAAU,QAAQ,KAAA,KAAU,MAAA;AACrC;AAEA,MAAM,SAAgC,CAAC;AAAA,EACrC,IAAA,GAAO,SAAA;AAAA,EACP,IAAA,GAAO,SAAA;AAAA,EACP,OAAA,GAAU,SAAA;AAAA,EACV,OAAA,GAAU,KAAA;AAAA,EACV,IAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAU,WAAW,IAAI,CAAA;AAC/B,EAAA,MAAM,WAAA,GAAc,WAAW,QAAQ,CAAA;AAEvC,EAAA,MAAM,UAAA,GACJ,WAAW,CAAC,WAAA;AAEd,EAAA,MAAM,aACJ,QAAA,IAAY,OAAA;AAEd,EAAA,MAAM,OAAA,GAAUA,qBAAA;AAAA,IACd,QAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,MACE,mBACE,IAAA,KAAS,SAAA;AAAA,MACX,gBACE,IAAA,KAAS,MAAA;AAAA,MACX,mBACE,OAAA,KAAY,SAAA;AAAA,MACd,mBACE,IAAA,KAAS,SAAA;AAAA,MACX,kBAAA,EACE,UAAA;AAAA,MACF,iBAAA,EACE,OAAA;AAAA,MACF,mBAAA,EACE;AAAA;AACJ,GACF;AAEA,EAAA,IAAI,YAAA,GAAe,IAAA;AAEnB,EAAA,IACEC,oBAAA,CAAe,IAAI,CAAA,IACnB,IAAA,CAAK,SAASC,qBAAA,EACd;AACA,IAAA,MAAM,WAAA,GACJ,IAAA;AAEF,IAAA,YAAA,GAAeC,kBAAA;AAAA,MACb,WAAA;AAAA,MACA;AAAA,QACE,IAAA,EACE,WAAA,CAAY,KAAA,CAAM,IAAA,IAClB,MAAA;AAAA,QACF,SAAA,EAAWH,qBAAA;AAAA,UACT,cAAA;AAAA,UACA,YAAY,KAAA,CAAM;AAAA,SACpB;AAAA,QACA,aAAA,EACE,WAAA,CAAY,KAAA,CACV,aACF,CAAA,IAAK,IAAA;AAAA,QACP,SAAA,EACE,WAAA,CAAY,KAAA,CACT,SAAA,IAAa;AAAA;AACpB,KACF;AAAA,EACF;AAEA,EAAA,MAAM,gBAAA,GACJ,OAAA,GACI,IAAA,GACA,KAAA,CAAM,WAAW,CAAA;AAEvB,EAAA,uBACEI,eAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,SAAA,EAAW,OAAA;AAAA,MACX,QAAA,EAAU,UAAA;AAAA,MACV,WAAA,EAAW,gBAAA;AAAA,MAEV,QAAA,EAAA;AAAA,QAAA,YAAA;AAAA,QACA;AAAA;AAAA;AAAA,GACH;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"button.cjs","sourcesContent":["import React, {\n cloneElement,\n isValidElement,\n type ReactElement,\n} from 'react';\n\nimport './button.css';\nimport Icon, { type IconProps } from '../../icons/icon';\nimport { classnames } from '../helpers/classnames';\n\nexport type ButtonRank =\n | 'default'\n | 'primary'\n | 'link';\n\nexport type ButtonDensity =\n | 'default'\n | 'compact';\n\ntype ButtonNativeProps = Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n>;\n\ninterface ButtonSharedProps\n extends ButtonNativeProps {\n density?: ButtonDensity;\n loading?: boolean;\n}\n\ntype RegularButtonContentProps =\n | {\n children: React.ReactNode;\n icon?: React.ReactNode;\n }\n | {\n children?: never;\n icon: React.ReactNode;\n 'aria-label': string;\n };\n\ntype RegularButtonProps =\n ButtonSharedProps &\n RegularButtonContentProps & {\n mode?: 'default';\n rank?: ButtonRank;\n };\n\ntype ToolbarButtonProps =\n ButtonSharedProps & {\n mode: 'toolbar';\n icon: React.ReactNode;\n children?: never;\n rank?: never;\n 'aria-label': string;\n };\n\nexport type ButtonProps =\n | RegularButtonProps\n | ToolbarButtonProps;\n\nfunction hasContent(\n value: React.ReactNode,\n): value is Exclude<\n React.ReactNode,\n null | undefined\n> {\n return value !== null && value !== undefined;\n}\n\nconst Button: React.FC<ButtonProps> = ({\n mode = 'default',\n rank = 'default',\n density = 'default',\n loading = false,\n icon,\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const hasIcon = hasContent(icon);\n const hasChildren = hasContent(children);\n\n const isIconOnly =\n hasIcon && !hasChildren;\n\n const isDisabled =\n disabled || loading;\n\n const classes = classnames(\n 'button',\n className,\n {\n 'button--primary':\n rank === 'primary',\n 'button--link':\n rank === 'link',\n 'button--compact':\n density === 'compact',\n 'button--toolbar':\n mode === 'toolbar',\n 'button--disabled':\n isDisabled,\n 'button--loading':\n loading,\n 'button--icon-only':\n isIconOnly,\n },\n );\n\n let resolvedIcon = icon;\n\n if (\n isValidElement(icon) &&\n icon.type === Icon\n ) {\n const iconElement =\n icon as ReactElement<IconProps>;\n\n resolvedIcon = cloneElement(\n iconElement,\n {\n size:\n iconElement.props.size ??\n 'base',\n className: classnames(\n 'button__icon',\n iconElement.props.className,\n ),\n 'aria-hidden':\n iconElement.props[\n 'aria-hidden'\n ] ?? true,\n focusable:\n iconElement.props\n .focusable ?? false,\n },\n );\n }\n\n const resolvedAriaBusy =\n loading\n ? true\n : props['aria-busy'];\n\n return (\n <button\n {...props}\n className={classes}\n disabled={isDisabled}\n aria-busy={resolvedAriaBusy}\n >\n {resolvedIcon}\n {children}\n </button>\n );\n};\n\nexport default Button;\n"]}
@@ -1,9 +1,13 @@
1
1
  .button {
2
2
  /* color */
3
- --button-bg: var(--color-primary-2);
4
- --button-color: var(--color-text-primary-2);
3
+ --button-bg: var(--color-control);
4
+ --button-color: var(--color-text-control);
5
5
  --button-bg-disabled: var(--color-bg-disabled);
6
- --button-color-disabled: var(--color-text-disabled, var(--color-text-control));
6
+ --button-color-disabled: var(
7
+ --color-text-disabled,
8
+ var(--color-text-control)
9
+ );
10
+ --button-focus-color: var(--button-bg);
7
11
 
8
12
  background: var(--button-bg);
9
13
  color: var(--button-color);
@@ -13,7 +17,9 @@
13
17
  --button-font-weight: var(--font-control-weight);
14
18
  --button-font-size: var(--font-control);
15
19
  --button-line-height: var(--font-control-line);
16
- --button-letter-spacing: var(--font-control-letter-spacing);
20
+ --button-letter-spacing: var(
21
+ --font-control-letter-spacing
22
+ );
17
23
 
18
24
  font-family: var(--button-font-family);
19
25
  font-size: var(--button-font-size);
@@ -25,7 +31,9 @@
25
31
  --button-gap: var(--margin-icon);
26
32
  --button-height: var(--size-control-height);
27
33
  --button-padding-x: var(--padding-control-button-horz);
28
- --button-padding-icon-only: var(--padding-control-button-icon-horz);
34
+ --button-padding-icon-only: var(
35
+ --padding-control-button-icon-horz
36
+ );
29
37
 
30
38
  display: inline-flex;
31
39
  align-items: center;
@@ -52,7 +60,8 @@
52
60
 
53
61
  cursor: pointer;
54
62
  user-select: none;
55
- transition: opacity var(--button-transition-duration) ease;
63
+ transition:
64
+ opacity var(--button-transition-duration) ease;
56
65
  }
57
66
 
58
67
  .button__icon {
@@ -78,7 +87,10 @@
78
87
  }
79
88
 
80
89
  .button:focus-visible {
81
- outline: var(--button-focus-width) solid var(--button-bg);
90
+ outline:
91
+ var(--button-focus-width)
92
+ solid
93
+ var(--button-focus-color);
82
94
  outline-offset: var(--button-focus-offset);
83
95
  }
84
96
 
@@ -94,11 +106,72 @@
94
106
  cursor: not-allowed;
95
107
  }
96
108
 
109
+ /* Rank */
110
+
97
111
  .button--primary {
98
112
  --button-bg: var(--color-primary-2);
99
113
  --button-color: var(--color-text-primary-2);
114
+ --button-focus-color: var(--color-primary-2);
100
115
  }
101
116
 
117
+ .button--link {
118
+ --button-bg: transparent;
119
+ --button-color: var(--color-link);
120
+ --button-focus-color: var(--color-link);
121
+ }
122
+
123
+ .button--link:disabled,
124
+ .button--link.button--disabled {
125
+ background: transparent;
126
+ }
127
+
128
+ /* Density */
129
+
130
+ .button--compact {
131
+ /*
132
+ * Compact presentation is intentionally left
133
+ * token-driven but visually unfinished.
134
+ *
135
+ * Add compact control dimensions when the
136
+ * corresponding semantic tokens are available.
137
+ */
138
+ }
139
+
140
+ /* Mode */
141
+
142
+ .button--toolbar {
143
+ --button-bg: transparent;
144
+ --button-color: var(--color-text);
145
+ --button-focus-color: var(--color-text);
146
+
147
+ background: transparent;
148
+ padding-inline: 0;
149
+ }
150
+
151
+ .button--toolbar:disabled,
152
+ .button--toolbar.button--disabled {
153
+ background: transparent;
154
+ }
155
+
156
+ /* State */
157
+
158
+ .button--loading {
159
+ /*
160
+ * Loading behavior is already defined by the
161
+ * component contract:
162
+ * disabled + aria-busy.
163
+ *
164
+ * Spinner and visual treatment can be added
165
+ * later without changing the public API.
166
+ */
167
+ }
168
+
169
+ /* Content */
170
+
102
171
  .button--icon-only {
103
172
  padding-inline: var(--button-padding-icon-only);
173
+ }
174
+
175
+ .button--toolbar.button--icon-only {
176
+ padding-inline: 0;
104
177
  }
@@ -1,8 +1,31 @@
1
1
  import React from 'react';
2
2
  import './button.css';
3
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
4
- state?: 'default' | 'primary';
5
- icon?: React.ReactNode;
3
+ export type ButtonRank = 'default' | 'primary' | 'link';
4
+ export type ButtonDensity = 'default' | 'compact';
5
+ type ButtonNativeProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'>;
6
+ interface ButtonSharedProps extends ButtonNativeProps {
7
+ density?: ButtonDensity;
8
+ loading?: boolean;
6
9
  }
10
+ type RegularButtonContentProps = {
11
+ children: React.ReactNode;
12
+ icon?: React.ReactNode;
13
+ } | {
14
+ children?: never;
15
+ icon: React.ReactNode;
16
+ 'aria-label': string;
17
+ };
18
+ type RegularButtonProps = ButtonSharedProps & RegularButtonContentProps & {
19
+ mode?: 'default';
20
+ rank?: ButtonRank;
21
+ };
22
+ type ToolbarButtonProps = ButtonSharedProps & {
23
+ mode: 'toolbar';
24
+ icon: React.ReactNode;
25
+ children?: never;
26
+ rank?: never;
27
+ 'aria-label': string;
28
+ };
29
+ export type ButtonProps = RegularButtonProps | ToolbarButtonProps;
7
30
  declare const Button: React.FC<ButtonProps>;
8
31
  export default Button;
@@ -1,39 +1,64 @@
1
1
  import { jsxs } from 'react/jsx-runtime';
2
2
  import { isValidElement, cloneElement } from 'react';
3
3
  import './button.css';
4
- import { classnames } from '../helpers/classnames';
5
4
  import Icon from '../../icons/icon';
5
+ import { classnames } from '../helpers/classnames';
6
6
 
7
+ function hasContent(value) {
8
+ return value !== null && value !== void 0;
9
+ }
7
10
  const Button = ({
8
- state = "default",
11
+ mode = "default",
12
+ rank = "default",
13
+ density = "default",
14
+ loading = false,
9
15
  icon,
10
16
  className = "",
11
17
  disabled = false,
12
18
  children,
13
19
  ...props
14
20
  }) => {
15
- const isIconOnly = !!icon && !children;
16
- const classes = classnames("button", className, {
17
- "button--primary": state === "primary",
18
- "button--disabled": disabled,
19
- "button--icon-only": isIconOnly
20
- });
21
+ const hasIcon = hasContent(icon);
22
+ const hasChildren = hasContent(children);
23
+ const isIconOnly = hasIcon && !hasChildren;
24
+ const isDisabled = disabled || loading;
25
+ const classes = classnames(
26
+ "button",
27
+ className,
28
+ {
29
+ "button--primary": rank === "primary",
30
+ "button--link": rank === "link",
31
+ "button--compact": density === "compact",
32
+ "button--toolbar": mode === "toolbar",
33
+ "button--disabled": isDisabled,
34
+ "button--loading": loading,
35
+ "button--icon-only": isIconOnly
36
+ }
37
+ );
21
38
  let resolvedIcon = icon;
22
39
  if (isValidElement(icon) && icon.type === Icon) {
23
40
  const iconElement = icon;
24
- resolvedIcon = cloneElement(iconElement, {
25
- size: iconElement.props.size ?? "base",
26
- className: classnames("button__icon", iconElement.props.className),
27
- "aria-hidden": iconElement.props["aria-hidden"] ?? true,
28
- focusable: iconElement.props.focusable ?? false
29
- });
41
+ resolvedIcon = cloneElement(
42
+ iconElement,
43
+ {
44
+ size: iconElement.props.size ?? "base",
45
+ className: classnames(
46
+ "button__icon",
47
+ iconElement.props.className
48
+ ),
49
+ "aria-hidden": iconElement.props["aria-hidden"] ?? true,
50
+ focusable: iconElement.props.focusable ?? false
51
+ }
52
+ );
30
53
  }
54
+ const resolvedAriaBusy = loading ? true : props["aria-busy"];
31
55
  return /* @__PURE__ */ jsxs(
32
56
  "button",
33
57
  {
34
- className: classes,
35
- disabled,
36
58
  ...props,
59
+ className: classes,
60
+ disabled: isDisabled,
61
+ "aria-busy": resolvedAriaBusy,
37
62
  children: [
38
63
  resolvedIcon,
39
64
  children
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/button/button.tsx"],"names":[],"mappings":";;;;;;AAeA,MAAM,SAAgC,CAAC;AAAA,EACrC,KAAA,GAAQ,SAAA;AAAA,EACR,IAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,UAAA,GAAa,CAAC,CAAC,IAAA,IAAQ,CAAC,QAAA;AAE9B,EAAA,MAAM,OAAA,GAAU,UAAA,CAAW,QAAA,EAAU,SAAA,EAAW;AAAA,IAC9C,mBAAmB,KAAA,KAAU,SAAA;AAAA,IAC7B,kBAAA,EAAoB,QAAA;AAAA,IACpB,mBAAA,EAAqB;AAAA,GACtB,CAAA;AAED,EAAA,IAAI,YAAA,GAAe,IAAA;AAEnB,EAAA,IAAI,cAAA,CAAe,IAAI,CAAA,IAAK,IAAA,CAAK,SAAS,IAAA,EAAM;AAC9C,IAAA,MAAM,WAAA,GAAc,IAAA;AAEpB,IAAA,YAAA,GAAe,aAAa,WAAA,EAAa;AAAA,MACvC,IAAA,EAAM,WAAA,CAAY,KAAA,CAAM,IAAA,IAAQ,MAAA;AAAA,MAChC,SAAA,EAAW,UAAA,CAAW,cAAA,EAAgB,WAAA,CAAY,MAAM,SAAS,CAAA;AAAA,MACjE,aAAA,EAAe,WAAA,CAAY,KAAA,CAAM,aAAa,CAAA,IAAK,IAAA;AAAA,MACnD,SAAA,EAAW,WAAA,CAAY,KAAA,CAAM,SAAA,IAAa;AAAA,KAC3C,CAAA;AAAA,EACH;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,OAAA;AAAA,MACX,QAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,YAAA;AAAA,QACA;AAAA;AAAA;AAAA,GACH;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"button.js","sourcesContent":["import React, {\n cloneElement,\n isValidElement,\n type ReactElement,\n} from 'react';\nimport './button.css';\nimport { classnames } from '../helpers/classnames';\nimport Icon, { type IconProps } from '../../icons/icon';\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n state?: 'default' | 'primary';\n icon?: React.ReactNode;\n}\n\nconst Button: React.FC<ButtonProps> = ({\n state = 'default',\n icon,\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const isIconOnly = !!icon && !children;\n\n const classes = classnames('button', className, {\n 'button--primary': state === 'primary',\n 'button--disabled': disabled,\n 'button--icon-only': isIconOnly,\n });\n\n let resolvedIcon = icon;\n\n if (isValidElement(icon) && icon.type === Icon) {\n const iconElement = icon as ReactElement<IconProps>;\n\n resolvedIcon = cloneElement(iconElement, {\n size: iconElement.props.size ?? 'base',\n className: classnames('button__icon', iconElement.props.className),\n 'aria-hidden': iconElement.props['aria-hidden'] ?? true,\n focusable: iconElement.props.focusable ?? false,\n });\n }\n\n return (\n <button\n className={classes}\n disabled={disabled}\n {...props}\n >\n {resolvedIcon}\n {children}\n </button>\n );\n};\n\nexport default Button;"]}
1
+ {"version":3,"sources":["../../../src/components/button/button.tsx"],"names":[],"mappings":";;;;;;AA6DA,SAAS,WACP,KAAA,EAIA;AACA,EAAA,OAAO,KAAA,KAAU,QAAQ,KAAA,KAAU,MAAA;AACrC;AAEA,MAAM,SAAgC,CAAC;AAAA,EACrC,IAAA,GAAO,SAAA;AAAA,EACP,IAAA,GAAO,SAAA;AAAA,EACP,OAAA,GAAU,SAAA;AAAA,EACV,OAAA,GAAU,KAAA;AAAA,EACV,IAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAU,WAAW,IAAI,CAAA;AAC/B,EAAA,MAAM,WAAA,GAAc,WAAW,QAAQ,CAAA;AAEvC,EAAA,MAAM,UAAA,GACJ,WAAW,CAAC,WAAA;AAEd,EAAA,MAAM,aACJ,QAAA,IAAY,OAAA;AAEd,EAAA,MAAM,OAAA,GAAU,UAAA;AAAA,IACd,QAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,MACE,mBACE,IAAA,KAAS,SAAA;AAAA,MACX,gBACE,IAAA,KAAS,MAAA;AAAA,MACX,mBACE,OAAA,KAAY,SAAA;AAAA,MACd,mBACE,IAAA,KAAS,SAAA;AAAA,MACX,kBAAA,EACE,UAAA;AAAA,MACF,iBAAA,EACE,OAAA;AAAA,MACF,mBAAA,EACE;AAAA;AACJ,GACF;AAEA,EAAA,IAAI,YAAA,GAAe,IAAA;AAEnB,EAAA,IACE,cAAA,CAAe,IAAI,CAAA,IACnB,IAAA,CAAK,SAAS,IAAA,EACd;AACA,IAAA,MAAM,WAAA,GACJ,IAAA;AAEF,IAAA,YAAA,GAAe,YAAA;AAAA,MACb,WAAA;AAAA,MACA;AAAA,QACE,IAAA,EACE,WAAA,CAAY,KAAA,CAAM,IAAA,IAClB,MAAA;AAAA,QACF,SAAA,EAAW,UAAA;AAAA,UACT,cAAA;AAAA,UACA,YAAY,KAAA,CAAM;AAAA,SACpB;AAAA,QACA,aAAA,EACE,WAAA,CAAY,KAAA,CACV,aACF,CAAA,IAAK,IAAA;AAAA,QACP,SAAA,EACE,WAAA,CAAY,KAAA,CACT,SAAA,IAAa;AAAA;AACpB,KACF;AAAA,EACF;AAEA,EAAA,MAAM,gBAAA,GACJ,OAAA,GACI,IAAA,GACA,KAAA,CAAM,WAAW,CAAA;AAEvB,EAAA,uBACE,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,SAAA,EAAW,OAAA;AAAA,MACX,QAAA,EAAU,UAAA;AAAA,MACV,WAAA,EAAW,gBAAA;AAAA,MAEV,QAAA,EAAA;AAAA,QAAA,YAAA;AAAA,QACA;AAAA;AAAA;AAAA,GACH;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"button.js","sourcesContent":["import React, {\n cloneElement,\n isValidElement,\n type ReactElement,\n} from 'react';\n\nimport './button.css';\nimport Icon, { type IconProps } from '../../icons/icon';\nimport { classnames } from '../helpers/classnames';\n\nexport type ButtonRank =\n | 'default'\n | 'primary'\n | 'link';\n\nexport type ButtonDensity =\n | 'default'\n | 'compact';\n\ntype ButtonNativeProps = Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n>;\n\ninterface ButtonSharedProps\n extends ButtonNativeProps {\n density?: ButtonDensity;\n loading?: boolean;\n}\n\ntype RegularButtonContentProps =\n | {\n children: React.ReactNode;\n icon?: React.ReactNode;\n }\n | {\n children?: never;\n icon: React.ReactNode;\n 'aria-label': string;\n };\n\ntype RegularButtonProps =\n ButtonSharedProps &\n RegularButtonContentProps & {\n mode?: 'default';\n rank?: ButtonRank;\n };\n\ntype ToolbarButtonProps =\n ButtonSharedProps & {\n mode: 'toolbar';\n icon: React.ReactNode;\n children?: never;\n rank?: never;\n 'aria-label': string;\n };\n\nexport type ButtonProps =\n | RegularButtonProps\n | ToolbarButtonProps;\n\nfunction hasContent(\n value: React.ReactNode,\n): value is Exclude<\n React.ReactNode,\n null | undefined\n> {\n return value !== null && value !== undefined;\n}\n\nconst Button: React.FC<ButtonProps> = ({\n mode = 'default',\n rank = 'default',\n density = 'default',\n loading = false,\n icon,\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const hasIcon = hasContent(icon);\n const hasChildren = hasContent(children);\n\n const isIconOnly =\n hasIcon && !hasChildren;\n\n const isDisabled =\n disabled || loading;\n\n const classes = classnames(\n 'button',\n className,\n {\n 'button--primary':\n rank === 'primary',\n 'button--link':\n rank === 'link',\n 'button--compact':\n density === 'compact',\n 'button--toolbar':\n mode === 'toolbar',\n 'button--disabled':\n isDisabled,\n 'button--loading':\n loading,\n 'button--icon-only':\n isIconOnly,\n },\n );\n\n let resolvedIcon = icon;\n\n if (\n isValidElement(icon) &&\n icon.type === Icon\n ) {\n const iconElement =\n icon as ReactElement<IconProps>;\n\n resolvedIcon = cloneElement(\n iconElement,\n {\n size:\n iconElement.props.size ??\n 'base',\n className: classnames(\n 'button__icon',\n iconElement.props.className,\n ),\n 'aria-hidden':\n iconElement.props[\n 'aria-hidden'\n ] ?? true,\n focusable:\n iconElement.props\n .focusable ?? false,\n },\n );\n }\n\n const resolvedAriaBusy =\n loading\n ? true\n : props['aria-busy'];\n\n return (\n <button\n {...props}\n className={classes}\n disabled={isDisabled}\n aria-busy={resolvedAriaBusy}\n >\n {resolvedIcon}\n {children}\n </button>\n );\n};\n\nexport default Button;\n"]}
@@ -12,36 +12,49 @@ var React__default = /*#__PURE__*/_interopDefault(React);
12
12
  function hasContent(value) {
13
13
  return value !== null && value !== void 0;
14
14
  }
15
- function getInitialOpenItemId(items, defaultOpenId) {
16
- if (defaultOpenId === void 0) {
15
+ function normalizeOpenItemId(items, openId) {
16
+ if (openId === null || openId === void 0) {
17
17
  return null;
18
18
  }
19
19
  const hasMatchingItem = items.some(
20
- (item) => item.id === defaultOpenId
20
+ (item) => item.id === openId
21
21
  );
22
- return hasMatchingItem ? defaultOpenId : null;
22
+ return hasMatchingItem ? openId : null;
23
23
  }
24
24
  const FlipList = ({
25
25
  items,
26
+ openId,
26
27
  defaultOpenId,
27
- scrollToDefaultOpen = false,
28
+ onOpenChange,
29
+ scrollToInitialOpen = false,
28
30
  className = "",
29
31
  ...props
30
32
  }) => {
31
33
  const componentId = React__default.default.useId();
34
+ const isControlled = openId !== void 0;
32
35
  const initialOpenItemIdRef = React__default.default.useRef(
33
- getInitialOpenItemId(
36
+ normalizeOpenItemId(
34
37
  items,
35
- defaultOpenId
38
+ isControlled ? openId : defaultOpenId
36
39
  )
37
40
  );
38
- const shouldScrollInitiallyRef = React__default.default.useRef(scrollToDefaultOpen);
41
+ const shouldScrollInitiallyRef = React__default.default.useRef(scrollToInitialOpen);
39
42
  const initialOpenItemRef = React__default.default.useRef(
40
43
  null
41
44
  );
42
45
  const hasScrolledInitiallyRef = React__default.default.useRef(false);
43
- const [openItemId, setOpenItemId] = React__default.default.useState(
44
- initialOpenItemIdRef.current
46
+ const [
47
+ uncontrolledOpenItemId,
48
+ setUncontrolledOpenItemId
49
+ ] = React__default.default.useState(
50
+ () => normalizeOpenItemId(
51
+ items,
52
+ defaultOpenId
53
+ )
54
+ );
55
+ const resolvedOpenItemId = normalizeOpenItemId(
56
+ items,
57
+ isControlled ? openId : uncontrolledOpenItemId
45
58
  );
46
59
  React__default.default.useEffect(() => {
47
60
  if (hasScrolledInitiallyRef.current || !shouldScrollInitiallyRef.current || initialOpenItemIdRef.current === null || !initialOpenItemRef.current) {
@@ -62,9 +75,13 @@ const FlipList = ({
62
75
  {}
63
76
  );
64
77
  const toggleItem = (itemId) => {
65
- setOpenItemId(
66
- (current) => current === itemId ? null : itemId
67
- );
78
+ const nextOpenItemId = resolvedOpenItemId === itemId ? null : itemId;
79
+ if (!isControlled) {
80
+ setUncontrolledOpenItemId(
81
+ nextOpenItemId
82
+ );
83
+ }
84
+ onOpenChange?.(nextOpenItemId);
68
85
  };
69
86
  return /* @__PURE__ */ jsxRuntime.jsx(
70
87
  "div",
@@ -72,7 +89,10 @@ const FlipList = ({
72
89
  className: classes,
73
90
  ...props,
74
91
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flip-list__items", children: items.map((item, itemIndex) => {
75
- const isOpen = openItemId === item.id;
92
+ const isOpen = resolvedOpenItemId === item.id;
93
+ const hasStatus = hasContent(
94
+ item.status
95
+ );
76
96
  const hasMeta = hasContent(
77
97
  item.meta
78
98
  );
@@ -101,8 +121,11 @@ const FlipList = ({
101
121
  "aria-controls": contentId,
102
122
  onClick: () => toggleItem(item.id),
103
123
  children: [
104
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flip-list__title", children: item.title }),
105
- hasMeta && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flip-list__meta", children: item.meta })
124
+ hasStatus && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flip-list__status", children: item.status }),
125
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flip-list__main", children: [
126
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flip-list__title", children: item.title }),
127
+ hasMeta && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flip-list__meta", children: item.meta })
128
+ ] })
106
129
  ]
107
130
  }
108
131
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/flip-list/flip-list.tsx"],"names":["React","classnames","jsx","jsxs"],"mappings":"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","file":"flip-list.cjs","sourcesContent":["import React from 'react';\n\nimport './flip-list.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface FlipListItem {\n id: React.Key;\n title: React.ReactNode;\n meta?: React.ReactNode;\n content: React.ReactNode;\n}\n\nexport interface FlipListProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children'\n > {\n items: FlipListItem[];\n\n /**\n * Item opened during the initial render.\n *\n * Later changes to this prop do not control\n * the component state.\n */\n defaultOpenId?: React.Key;\n\n /**\n * Scrolls the initially opened item into view once\n * after mounting.\n *\n * Has no effect without a valid defaultOpenId.\n * Manual item opening never triggers scrolling.\n */\n scrollToDefaultOpen?: boolean;\n}\n\ntype OpenItemId = React.Key | null;\n\nfunction hasContent(\n value: React.ReactNode,\n): value is Exclude<\n React.ReactNode,\n null | undefined\n> {\n return value !== null && value !== undefined;\n}\n\nfunction getInitialOpenItemId(\n items: FlipListItem[],\n defaultOpenId: React.Key | undefined,\n): OpenItemId {\n if (defaultOpenId === undefined) {\n return null;\n }\n\n const hasMatchingItem = items.some(\n (item) => item.id === defaultOpenId,\n );\n\n return hasMatchingItem\n ? defaultOpenId\n : null;\n}\n\nconst FlipList: React.FC<FlipListProps> = ({\n items,\n defaultOpenId,\n scrollToDefaultOpen = false,\n className = '',\n ...props\n}) => {\n const componentId = React.useId();\n\n const initialOpenItemIdRef =\n React.useRef<OpenItemId>(\n getInitialOpenItemId(\n items,\n defaultOpenId,\n ),\n );\n\n const shouldScrollInitiallyRef =\n React.useRef(scrollToDefaultOpen);\n\n const initialOpenItemRef =\n React.useRef<HTMLDivElement | null>(\n null,\n );\n\n const hasScrolledInitiallyRef =\n React.useRef(false);\n\n const [openItemId, setOpenItemId] =\n React.useState<OpenItemId>(\n initialOpenItemIdRef.current,\n );\n\n React.useEffect(() => {\n if (\n hasScrolledInitiallyRef.current ||\n !shouldScrollInitiallyRef.current ||\n initialOpenItemIdRef.current === null ||\n !initialOpenItemRef.current\n ) {\n return;\n }\n\n hasScrolledInitiallyRef.current = true;\n\n initialOpenItemRef.current.scrollIntoView({\n behavior: 'auto',\n block: 'start',\n });\n }, []);\n\n if (items.length === 0) {\n return null;\n }\n\n const classes = classnames(\n 'flip-list',\n className,\n {},\n );\n\n const toggleItem = (\n itemId: React.Key,\n ) => {\n setOpenItemId((current) =>\n current === itemId\n ? null\n : itemId,\n );\n };\n\n return (\n <div\n className={classes}\n {...props}\n >\n <div className=\"flip-list__items\">\n {items.map((item, itemIndex) => {\n const isOpen =\n openItemId === item.id;\n\n const hasMeta = hasContent(\n item.meta,\n );\n\n const triggerId =\n `${componentId}-trigger-${itemIndex}`;\n\n const contentId =\n `${componentId}-content-${itemIndex}`;\n\n const itemClasses = classnames(\n 'flip-list__item',\n '',\n {\n 'flip-list__item--open':\n isOpen,\n },\n );\n\n return (\n <div\n key={item.id}\n ref={\n item.id ===\n initialOpenItemIdRef.current\n ? initialOpenItemRef\n : undefined\n }\n className={itemClasses}\n >\n <button\n className=\"flip-list__trigger\"\n id={triggerId}\n type=\"button\"\n aria-expanded={isOpen}\n aria-controls={contentId}\n onClick={() =>\n toggleItem(item.id)\n }\n >\n <span className=\"flip-list__title\">\n {item.title}\n </span>\n\n {hasMeta && (\n <span className=\"flip-list__meta\">\n {item.meta}\n </span>\n )}\n </button>\n\n <div\n className=\"flip-list__content\"\n id={contentId}\n aria-labelledby={triggerId}\n hidden={!isOpen}\n >\n {item.content}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default FlipList;"]}
1
+ {"version":3,"sources":["../../../src/components/flip-list/flip-list.tsx"],"names":["React","classnames","jsx","jsxs"],"mappings":"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","file":"flip-list.cjs","sourcesContent":["import React from 'react';\n\nimport './flip-list.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface FlipListItem {\n id: React.Key;\n title: React.ReactNode;\n status?: React.ReactNode;\n meta?: React.ReactNode;\n content: React.ReactNode;\n}\n\nexport interface FlipListProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children'\n > {\n items: FlipListItem[];\n\n /**\n * Currently opened item in controlled mode.\n *\n * `null` means that all items are closed.\n *\n * When this prop is provided, FlipList does not\n * own the opened-item state.\n */\n openId?: React.Key | null;\n\n /**\n * Item opened during the initial render\n * in uncontrolled mode.\n *\n * Later changes to this prop do not control\n * the component state.\n */\n defaultOpenId?: React.Key;\n\n /**\n * Called when the user requests a change\n * of the opened item.\n *\n * Receives the item id when an item is opened,\n * and `null` when the currently opened item\n * is closed.\n */\n onOpenChange?: (\n id: React.Key | null,\n ) => void;\n\n /**\n * Scrolls the item that is open during\n * the initial render into view once.\n *\n * In uncontrolled mode, the initial item comes\n * from `defaultOpenId`.\n *\n * In controlled mode, the initial item comes\n * from `openId`.\n *\n * Later state changes never trigger scrolling.\n */\n scrollToInitialOpen?: boolean;\n}\n\ntype OpenItemId = React.Key | null;\n\nfunction hasContent(\n value: React.ReactNode,\n): value is Exclude<\n React.ReactNode,\n null | undefined\n> {\n return value !== null && value !== undefined;\n}\n\nfunction normalizeOpenItemId(\n items: FlipListItem[],\n openId: OpenItemId | undefined,\n): OpenItemId {\n if (\n openId === null ||\n openId === undefined\n ) {\n return null;\n }\n\n const hasMatchingItem = items.some(\n (item) => item.id === openId,\n );\n\n return hasMatchingItem\n ? openId\n : null;\n}\n\nconst FlipList: React.FC<FlipListProps> = ({\n items,\n openId,\n defaultOpenId,\n onOpenChange,\n scrollToInitialOpen = false,\n className = '',\n ...props\n}) => {\n const componentId = React.useId();\n\n const isControlled =\n openId !== undefined;\n\n const initialOpenItemIdRef =\n React.useRef<OpenItemId>(\n normalizeOpenItemId(\n items,\n isControlled\n ? openId\n : defaultOpenId,\n ),\n );\n\n const shouldScrollInitiallyRef =\n React.useRef(scrollToInitialOpen);\n\n const initialOpenItemRef =\n React.useRef<HTMLDivElement | null>(\n null,\n );\n\n const hasScrolledInitiallyRef =\n React.useRef(false);\n\n const [\n uncontrolledOpenItemId,\n setUncontrolledOpenItemId,\n ] = React.useState<OpenItemId>(() =>\n normalizeOpenItemId(\n items,\n defaultOpenId,\n ),\n );\n\n const resolvedOpenItemId =\n normalizeOpenItemId(\n items,\n isControlled\n ? openId\n : uncontrolledOpenItemId,\n );\n\n React.useEffect(() => {\n if (\n hasScrolledInitiallyRef.current ||\n !shouldScrollInitiallyRef.current ||\n initialOpenItemIdRef.current === null ||\n !initialOpenItemRef.current\n ) {\n return;\n }\n\n hasScrolledInitiallyRef.current = true;\n\n initialOpenItemRef.current.scrollIntoView({\n behavior: 'auto',\n block: 'start',\n });\n }, []);\n\n if (items.length === 0) {\n return null;\n }\n\n const classes = classnames(\n 'flip-list',\n className,\n {},\n );\n\n const toggleItem = (\n itemId: React.Key,\n ) => {\n const nextOpenItemId =\n resolvedOpenItemId === itemId\n ? null\n : itemId;\n\n if (!isControlled) {\n setUncontrolledOpenItemId(\n nextOpenItemId,\n );\n }\n\n onOpenChange?.(nextOpenItemId);\n };\n\n return (\n <div\n className={classes}\n {...props}\n >\n <div className=\"flip-list__items\">\n {items.map((item, itemIndex) => {\n const isOpen =\n resolvedOpenItemId === item.id;\n\n const hasStatus = hasContent(\n item.status,\n );\n\n const hasMeta = hasContent(\n item.meta,\n );\n\n const triggerId =\n `${componentId}-trigger-${itemIndex}`;\n\n const contentId =\n `${componentId}-content-${itemIndex}`;\n\n const itemClasses = classnames(\n 'flip-list__item',\n '',\n {\n 'flip-list__item--open':\n isOpen,\n },\n );\n\n return (\n <div\n key={item.id}\n ref={\n item.id ===\n initialOpenItemIdRef.current\n ? initialOpenItemRef\n : undefined\n }\n className={itemClasses}\n >\n <button\n className=\"flip-list__trigger\"\n id={triggerId}\n type=\"button\"\n aria-expanded={isOpen}\n aria-controls={contentId}\n onClick={() =>\n toggleItem(item.id)\n }\n >\n {hasStatus && (\n <span className=\"flip-list__status\">\n {item.status}\n </span>\n )}\n\n <span className=\"flip-list__main\">\n <span className=\"flip-list__title\">\n {item.title}\n </span>\n\n {hasMeta && (\n <span className=\"flip-list__meta\">\n {item.meta}\n </span>\n )}\n </span>\n </button>\n\n <div\n className=\"flip-list__content\"\n id={contentId}\n aria-labelledby={triggerId}\n hidden={!isOpen}\n >\n {item.content}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default FlipList;\n"]}
@@ -133,9 +133,8 @@
133
133
 
134
134
  /* size */
135
135
  display: flex;
136
- align-items: flex-start;
137
- justify-content: space-between;
138
- gap: var(--flip-list-trigger-column-gap);
136
+ flex-direction: column;
137
+ align-items: stretch;
139
138
  width: 100%;
140
139
  min-width: 0;
141
140
  max-width: 100%;
@@ -170,6 +169,53 @@
170
169
  /* motion */
171
170
  }
172
171
 
172
+ .flip-list__status {
173
+ /* color */
174
+
175
+ /* typography */
176
+
177
+ /* size */
178
+ --flip-list-status-gap:
179
+ var(--margin-tag);
180
+
181
+ display: block;
182
+ align-self: flex-start;
183
+ min-width: 0;
184
+ max-width: 100%;
185
+ margin:
186
+ 0
187
+ 0
188
+ var(--flip-list-status-gap)
189
+ 0;
190
+
191
+ /* border */
192
+
193
+ /* depth */
194
+
195
+ /* motion */
196
+ }
197
+
198
+ .flip-list__main {
199
+ /* color */
200
+
201
+ /* typography */
202
+
203
+ /* size */
204
+ display: flex;
205
+ align-items: flex-start;
206
+ justify-content: space-between;
207
+ gap: var(--flip-list-trigger-column-gap);
208
+ width: 100%;
209
+ min-width: 0;
210
+ max-width: 100%;
211
+
212
+ /* border */
213
+
214
+ /* depth */
215
+
216
+ /* motion */
217
+ }
218
+
173
219
  .flip-list__title {
174
220
  /* color */
175
221
  color: inherit;
@@ -3,26 +3,51 @@ import './flip-list.css';
3
3
  export interface FlipListItem {
4
4
  id: React.Key;
5
5
  title: React.ReactNode;
6
+ status?: React.ReactNode;
6
7
  meta?: React.ReactNode;
7
8
  content: React.ReactNode;
8
9
  }
9
10
  export interface FlipListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
10
11
  items: FlipListItem[];
11
12
  /**
12
- * Item opened during the initial render.
13
+ * Currently opened item in controlled mode.
14
+ *
15
+ * `null` means that all items are closed.
16
+ *
17
+ * When this prop is provided, FlipList does not
18
+ * own the opened-item state.
19
+ */
20
+ openId?: React.Key | null;
21
+ /**
22
+ * Item opened during the initial render
23
+ * in uncontrolled mode.
13
24
  *
14
25
  * Later changes to this prop do not control
15
26
  * the component state.
16
27
  */
17
28
  defaultOpenId?: React.Key;
18
29
  /**
19
- * Scrolls the initially opened item into view once
20
- * after mounting.
30
+ * Called when the user requests a change
31
+ * of the opened item.
32
+ *
33
+ * Receives the item id when an item is opened,
34
+ * and `null` when the currently opened item
35
+ * is closed.
36
+ */
37
+ onOpenChange?: (id: React.Key | null) => void;
38
+ /**
39
+ * Scrolls the item that is open during
40
+ * the initial render into view once.
41
+ *
42
+ * In uncontrolled mode, the initial item comes
43
+ * from `defaultOpenId`.
44
+ *
45
+ * In controlled mode, the initial item comes
46
+ * from `openId`.
21
47
  *
22
- * Has no effect without a valid defaultOpenId.
23
- * Manual item opening never triggers scrolling.
48
+ * Later state changes never trigger scrolling.
24
49
  */
25
- scrollToDefaultOpen?: boolean;
50
+ scrollToInitialOpen?: boolean;
26
51
  }
27
52
  declare const FlipList: React.FC<FlipListProps>;
28
53
  export default FlipList;