@designbasekorea/figma-ui 0.1.80 → 0.1.82

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.
package/dist/index.js CHANGED
@@ -33,14 +33,15 @@ const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', te
33
33
  };
34
34
  DonationBadge.displayName = 'DonationBadge';
35
35
 
36
- const FigmaContainer = ({ header, children, interactionFeedback, footer, headerHeight = 52, interactionHeight = 40, footerHeight = 60, footerMinHeight = 40, footerMaxHeight = 200, footerResizable = false, size = 'm', fullHeight = true, className, }) => {
36
+ const FigmaContainer = ({ header, children, interactionFeedback, actionButton, footer, headerHeight = 52, interactionHeight = 40, actionButtonHeight = 52, footerHeight = 60, footerMinHeight = 40, footerMaxHeight = 200, footerResizable = false, size = 'm', fullHeight = true, className, }) => {
37
37
  const classes = clsx('designbase-figma-container', `designbase-figma-container--${size}`, {
38
38
  'designbase-figma-container--full-height': fullHeight,
39
39
  'designbase-figma-container--with-header': !!header,
40
40
  'designbase-figma-container--with-interaction': !!interactionFeedback,
41
+ 'designbase-figma-container--with-action': !!actionButton,
41
42
  'designbase-figma-container--with-footer': !!footer,
42
43
  }, className);
43
- if (!header && !interactionFeedback && !footer) {
44
+ if (!header && !interactionFeedback && !actionButton && !footer) {
44
45
  return (React.createElement("div", { className: classes },
45
46
  React.createElement("div", { className: "designbase-figma-container__main" }, children)));
46
47
  }
@@ -71,6 +72,16 @@ const FigmaContainer = ({ header, children, interactionFeedback, footer, headerH
71
72
  resizable: false,
72
73
  });
73
74
  }
75
+ if (actionButton) {
76
+ panels.push({
77
+ content: React.createElement("div", { className: "designbase-figma-container__action" }, actionButton),
78
+ size: actionButtonHeight,
79
+ minSize: actionButtonHeight,
80
+ maxSize: actionButtonHeight,
81
+ flexible: false,
82
+ resizable: false,
83
+ });
84
+ }
74
85
  if (footer) {
75
86
  panels.push({
76
87
  content: React.createElement("div", { className: "designbase-figma-container__footer" }, footer),
@@ -102,69 +113,110 @@ const FigmaHeader = ({ children, actions = [], searchBar, sticky = true, classNa
102
113
  };
103
114
  FigmaHeader.displayName = 'FigmaHeader';
104
115
 
105
- const FigmaSection = ({ title, dataCategory, iconButton, children, marginBottom, isEnabled = true, onToggle, className, enableScrollNavigation = false, headerHeight = 94, onActiveSectionChange, t, collapsible = false, defaultCollapsed = false, onCollapseChange }) => {
116
+ const FigmaSection = ({ title, dataCategory, iconButton, children, marginBottom, isEnabled = true, onToggle, className, enableScrollNavigation = false, headerHeight = 94, onActiveSectionChange, t, collapsible = false, defaultCollapsed = false, onCollapseChange, }) => {
106
117
  const sectionRef = React.useRef(null);
107
- const contentInnerRef = React.useRef(null);
108
- const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed);
109
- const sectionStyle = marginBottom ? { marginBottom: `${marginBottom}px` } : {};
118
+ const clipRef = React.useRef(null);
119
+ const innerRef = React.useRef(null);
120
+ const animatingRef = React.useRef(false);
121
+ const [collapsed, setCollapsed] = React.useState(defaultCollapsed);
122
+ const sectionStyle = marginBottom ? { marginBottom: `${marginBottom}px` } : undefined;
110
123
  const classes = [
111
124
  'designbase-figma-section',
112
125
  !isEnabled && 'designbase-figma-section--disabled',
113
126
  collapsible && 'designbase-figma-section--collapsible',
114
- isCollapsed && collapsible && 'designbase-figma-section--collapsed',
127
+ collapsed && collapsible && 'designbase-figma-section--collapsed',
115
128
  className,
116
- ]
117
- .filter(Boolean)
118
- .join(' ');
119
- const handleHeaderClick = () => {
120
- if (collapsible) {
121
- const newCollapsed = !isCollapsed;
122
- setIsCollapsed(newCollapsed);
123
- if (onCollapseChange) {
124
- onCollapseChange(newCollapsed, dataCategory);
125
- }
129
+ ].filter(Boolean).join(' ');
130
+ const toggle = () => {
131
+ if (!collapsible)
132
+ return;
133
+ setCollapsed(v => {
134
+ const next = !v;
135
+ onCollapseChange?.(next, dataCategory);
136
+ animate(next);
137
+ return next;
138
+ });
139
+ };
140
+ const animate = (toCollapsed) => {
141
+ const clip = clipRef.current;
142
+ const inner = innerRef.current;
143
+ if (!clip || !inner)
144
+ return;
145
+ const transitionMs = 250;
146
+ const setH = (h) => { clip.style.height = h; };
147
+ if (animatingRef.current) {
148
+ const current = `${clip.getBoundingClientRect().height}px`;
149
+ clip.style.transition = 'none';
150
+ setH(current);
151
+ clip.offsetHeight;
152
+ clip.style.transition = '';
153
+ }
154
+ animatingRef.current = true;
155
+ if (toCollapsed) {
156
+ const start = `${inner.scrollHeight}px`;
157
+ setH(start);
158
+ clip.offsetHeight;
159
+ setH('0px');
160
+ }
161
+ else {
162
+ setH('0px');
163
+ clip.offsetHeight;
164
+ setH(`${inner.scrollHeight}px`);
126
165
  }
166
+ const onEnd = (e) => {
167
+ if (e.propertyName !== 'height')
168
+ return;
169
+ clip.removeEventListener('transitionend', onEnd);
170
+ if (!toCollapsed) {
171
+ setH('auto');
172
+ }
173
+ animatingRef.current = false;
174
+ };
175
+ clip.addEventListener('transitionend', onEnd);
176
+ window.setTimeout(() => {
177
+ if (!animatingRef.current)
178
+ return;
179
+ if (!toCollapsed)
180
+ setH('auto');
181
+ animatingRef.current = false;
182
+ }, transitionMs + 50);
127
183
  };
128
184
  React.useEffect(() => {
129
- if (collapsible && contentInnerRef.current) {
130
- if (!isCollapsed) {
131
- contentInnerRef.current.style.maxHeight = `${contentInnerRef.current.scrollHeight}px`;
132
- }
133
- else {
134
- contentInnerRef.current.style.maxHeight = '0px';
135
- }
136
- }
137
- }, [isCollapsed, collapsible]);
185
+ const clip = clipRef.current;
186
+ const inner = innerRef.current;
187
+ if (!clip || !inner)
188
+ return;
189
+ clip.style.height = collapsed ? '0px' : 'auto';
190
+ }, []);
191
+ React.useEffect(() => {
192
+ if (collapsed)
193
+ return;
194
+ const clip = clipRef.current;
195
+ const inner = innerRef.current;
196
+ if (!clip || !inner)
197
+ return;
198
+ if (clip.style.height === 'auto')
199
+ return;
200
+ clip.style.height = `${inner.scrollHeight}px`;
201
+ }, [children, collapsed]);
138
202
  React.useEffect(() => {
139
203
  if (!enableScrollNavigation || !onActiveSectionChange || !sectionRef.current)
140
204
  return;
141
- const observerOptions = {
142
- root: null,
143
- rootMargin: `-${headerHeight}px 0px -50% 0px`,
144
- threshold: 0
145
- };
146
- const observer = new IntersectionObserver((entries) => {
147
- entries.forEach((entry) => {
148
- if (entry.isIntersecting) {
149
- onActiveSectionChange(dataCategory);
150
- }
151
- });
152
- }, observerOptions);
205
+ const observer = new IntersectionObserver(entries => entries.forEach(entry => entry.isIntersecting && onActiveSectionChange(dataCategory)), { root: null, rootMargin: `-${headerHeight}px 0px -50% 0px`, threshold: 0 });
153
206
  observer.observe(sectionRef.current);
154
- return () => {
155
- observer.disconnect();
156
- };
207
+ return () => observer.disconnect();
157
208
  }, [enableScrollNavigation, dataCategory, headerHeight, onActiveSectionChange]);
158
209
  return (React.createElement("section", { ref: sectionRef, className: classes, ...(dataCategory ? { 'data-category': dataCategory } : {}), style: sectionStyle },
159
- title && (React.createElement("div", { className: "designbase-figma-section__header", onClick: collapsible ? handleHeaderClick : undefined, style: collapsible ? { cursor: 'pointer' } : undefined },
210
+ title && (React.createElement("div", { className: "designbase-figma-section__header", onClick: collapsible ? toggle : undefined, style: collapsible ? { cursor: 'pointer' } : undefined },
160
211
  React.createElement("h2", { className: "designbase-figma-section__title" }, resolveText(t, title)),
161
212
  React.createElement("div", { className: "designbase-figma-section__controls" },
162
213
  iconButton,
163
- collapsible && (React.createElement("span", { className: `designbase-figma-section__collapse-icon ${isCollapsed ? 'designbase-figma-section__collapse-icon--collapsed' : ''}` },
214
+ collapsible && (React.createElement("span", { className: `designbase-figma-section__collapse-icon ${collapsed ? 'designbase-figma-section__collapse-icon--collapsed' : ''}` },
164
215
  React.createElement(icons.ChevronDownIcon, { size: 16 }))),
165
216
  onToggle && (React.createElement(ui.Toggle, { checked: isEnabled, onChange: () => onToggle(dataCategory), size: "s" }))))),
166
- React.createElement("div", { className: `designbase-figma-section__content ${!isEnabled ? 'designbase-figma-section__content--hidden' : ''} ${isCollapsed && collapsible ? 'designbase-figma-section__content--collapsed' : ''}` },
167
- React.createElement("div", { ref: contentInnerRef, className: "designbase-figma-section__content-inner" }, children))));
217
+ React.createElement("div", { className: `designbase-figma-section__content ${!isEnabled ? 'designbase-figma-section__content--hidden' : ''}` },
218
+ React.createElement("div", { ref: clipRef, className: "designbase-figma-section__content-clip" },
219
+ React.createElement("div", { ref: innerRef, className: `designbase-figma-section__content-inner ${collapsible && collapsed ? 'is-collapsed' : ''}` }, children)))));
168
220
  };
169
221
  FigmaSection.displayName = 'FigmaSection';
170
222
  const scrollToSection = (category, headerHeight = 94) => {