@syscore/ui-library 1.10.0 → 1.10.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.
@@ -33,6 +33,8 @@ interface AccordionItemContextValue {
33
33
  value: string;
34
34
  /** Whether this item is expanded */
35
35
  isExpanded: boolean;
36
+ /** Whether to remove borders when open */
37
+ noBorderOnOpen?: boolean;
36
38
  }
37
39
 
38
40
  const AccordionItemContext =
@@ -324,21 +326,32 @@ AccordionHeaderRow.displayName = "AccordionHeaderRow";
324
326
  interface AccordionItemProps extends React.ComponentPropsWithoutRef<"div"> {
325
327
  /** Unique value for this item */
326
328
  value: string;
329
+ /** Remove bottom border when the accordion item is open */
330
+ noBorderOnOpen?: boolean;
327
331
  }
328
332
 
329
333
  const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
330
- ({ value, children, className, ...props }, ref) => {
334
+ ({ value, children, className, noBorderOnOpen, ...props }, ref) => {
331
335
  const { isExpanded: isExpandedFn } = useAccordion();
332
336
  const isExpanded = isExpandedFn(value);
333
337
 
334
338
  const itemContextValue = React.useMemo(
335
- () => ({ value, isExpanded }),
336
- [value, isExpanded],
339
+ () => ({ value, isExpanded, noBorderOnOpen }),
340
+ [value, isExpanded, noBorderOnOpen],
337
341
  );
338
342
 
339
343
  return (
340
344
  <AccordionItemContext.Provider value={itemContextValue}>
341
- <div ref={ref} className={cn(className)} {...props}>
345
+ <div
346
+ ref={ref}
347
+ className={cn(className)}
348
+ style={
349
+ noBorderOnOpen && isExpanded
350
+ ? { borderBottom: "none" }
351
+ : undefined
352
+ }
353
+ {...props}
354
+ >
342
355
  {children}
343
356
  </div>
344
357
  </AccordionItemContext.Provider>
@@ -349,12 +362,26 @@ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
349
362
  AccordionItem.displayName = "AccordionItem";
350
363
 
351
364
  // AccordionHeader
352
- type AccordionHeaderProps = React.ComponentPropsWithoutRef<"div">;
365
+ type AccordionHeaderProps = React.ComponentPropsWithoutRef<"div"> & {
366
+ /** Remove background color when the accordion item is open */
367
+ transparentOnOpen?: boolean;
368
+ };
353
369
 
354
370
  const AccordionHeader = React.forwardRef<HTMLDivElement, AccordionHeaderProps>(
355
- ({ children, className, onClick, ...props }, ref) => {
371
+ ({ children, className, onClick, transparentOnOpen, ...props }, ref) => {
372
+ const { isExpanded } = useAccordionItem();
356
373
  return (
357
- <div ref={ref} onClick={onClick} className={cn(className)} {...props}>
374
+ <div
375
+ ref={ref}
376
+ onClick={onClick}
377
+ className={cn(className)}
378
+ style={
379
+ transparentOnOpen && isExpanded
380
+ ? { backgroundColor: "transparent" }
381
+ : undefined
382
+ }
383
+ {...props}
384
+ >
358
385
  {children}
359
386
  </div>
360
387
  );
@@ -409,7 +436,7 @@ type AccordionContentProps = React.ComponentPropsWithoutRef<"div">;
409
436
 
410
437
  const AccordionContent = React.forwardRef<HTMLDivElement, AccordionContentProps>(
411
438
  ({ children, className, ...props }, ref) => {
412
- const { isExpanded } = useAccordionItem();
439
+ const { isExpanded, noBorderOnOpen } = useAccordionItem();
413
440
 
414
441
  return (
415
442
  <AnimatePresence initial={false}>
@@ -420,7 +447,10 @@ const AccordionContent = React.forwardRef<HTMLDivElement, AccordionContentProps>
420
447
  exit={{ height: 0, opacity: 0 }}
421
448
  transition={{ duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] }}
422
449
  className={cn(className)}
423
- style={{ willChange: "opacity" }}
450
+ style={{
451
+ willChange: "opacity",
452
+ ...(noBorderOnOpen ? { borderTop: "none" } : {}),
453
+ }}
424
454
  >
425
455
  <div ref={ref} {...props}>
426
456
  {children}
@@ -195,6 +195,7 @@ export const IWBIConceptsTable: Story = {
195
195
  <AccordionHeader
196
196
  onClick={() => navigateTo("/")}
197
197
  className="standard-table-row-header"
198
+ transparentOnOpen
198
199
  >
199
200
  <Icon active={true} className="size-12 shrink-0" />
200
201
  <span className="overline-large flex-1">{concept.name}</span>
@@ -235,6 +236,190 @@ export const IWBIConceptsTable: Story = {
235
236
  },
236
237
  };
237
238
 
239
+ // Showcases transparentOnOpen and noBorderOnOpen props
240
+ export const TransparentOnOpen: Story = {
241
+ render: () => {
242
+ const [expandedConcepts, setExpandedConcepts] = useState<Set<string>>(
243
+ new Set(),
244
+ );
245
+
246
+ const hasAnyExpanded = expandedConcepts.size > 0;
247
+
248
+ const toggleAllConcepts = () => {
249
+ if (hasAnyExpanded) {
250
+ setExpandedConcepts(new Set());
251
+ } else {
252
+ setExpandedConcepts(new Set(concepts.map((c) => c.id)));
253
+ }
254
+ };
255
+
256
+ const navigateTo = (path: string) => {
257
+ console.log(path);
258
+ };
259
+
260
+ function getSlugFromName(name: string) {
261
+ return name.toLowerCase().replace(/\s+/g, "-");
262
+ }
263
+
264
+ return (
265
+ <div className="grid grid-cols-2 gap-4">
266
+ <div className="flex flex-col gap-2">
267
+ <div className="flex gap-2">
268
+ <code className="text-xs bg-gray-100 px-2 py-1 rounded">
269
+ AccordionItem: noBorderOnOpen
270
+ </code>
271
+ <code className="text-xs bg-gray-100 px-2 py-1 rounded">
272
+ AccordionHeader: transparentOnOpen
273
+ </code>
274
+ </div>
275
+ <AccordionContainer className="standard-table-container">
276
+ <AccordionSectionHeader
277
+ title="no border and transparent on open"
278
+ hasExpanded={hasAnyExpanded}
279
+ onToggleAll={toggleAllConcepts}
280
+ className="standard-table-header"
281
+ />
282
+
283
+ <Accordion
284
+ allowMultiple={true}
285
+ expandedValues={expandedConcepts}
286
+ onExpandedChange={setExpandedConcepts}
287
+ className="border border-blue-200 rounded-xl overflow-hidden"
288
+ >
289
+ {[...concepts].map((concept) => {
290
+ const slug = getSlugFromName(concept.name);
291
+ const Icon = CONCEPT_ICONS[slug];
292
+ const color = getConceptColor(slug);
293
+
294
+ return (
295
+ <AccordionItem
296
+ key={concept.id}
297
+ value={concept.id}
298
+ className="standard-table-row"
299
+ noBorderOnOpen
300
+ >
301
+ <AccordionHeader
302
+ onClick={() => navigateTo("/")}
303
+ className="standard-table-row-header"
304
+ transparentOnOpen
305
+ >
306
+ <Icon active={true} className="size-12 shrink-0" />
307
+ <span className="overline-large flex-1">
308
+ {concept.name}
309
+ </span>
310
+ <AccordionTrigger className="standard-table-trigger" />
311
+ </AccordionHeader>
312
+
313
+ <AccordionContent className="standard-table-content">
314
+ <div className="standard-table-content__inner">
315
+ {concept.themes.slice(0, 1).map((theme) => (
316
+ <AccordionListRow
317
+ key={theme.id}
318
+ badge={
319
+ <Tag
320
+ variant="code"
321
+ style={{
322
+ backgroundColor: color.contrast || color.solid,
323
+ borderColor: color.border,
324
+ color: "white",
325
+ }}
326
+ >
327
+ {theme.code}
328
+ </Tag>
329
+ }
330
+ title={theme.name}
331
+ titleClassName="standard-table-list-row__title body-large"
332
+ className="standard-table-list-row standard-table-list-row--nested"
333
+ onClick={() => navigateTo("/")}
334
+ />
335
+ ))}
336
+ </div>
337
+ </AccordionContent>
338
+ </AccordionItem>
339
+ );
340
+ })}
341
+ </Accordion>
342
+ </AccordionContainer>
343
+ </div>
344
+
345
+ <div className="flex flex-col gap-2">
346
+ <div className="flex gap-2">
347
+ <code className="text-xs bg-gray-100 px-2 py-1 rounded">
348
+ No props
349
+ </code>
350
+ </div>
351
+ <AccordionContainer className="standard-table-container">
352
+ <AccordionSectionHeader
353
+ title="Default"
354
+ hasExpanded={hasAnyExpanded}
355
+ onToggleAll={toggleAllConcepts}
356
+ className="standard-table-header"
357
+ />
358
+
359
+ <Accordion
360
+ allowMultiple={true}
361
+ expandedValues={expandedConcepts}
362
+ onExpandedChange={setExpandedConcepts}
363
+ className="border border-blue-200 rounded-xl overflow-hidden"
364
+ >
365
+ {[...concepts].map((concept) => {
366
+ const slug = getSlugFromName(concept.name);
367
+ const Icon = CONCEPT_ICONS[slug];
368
+ const color = getConceptColor(slug);
369
+
370
+ return (
371
+ <AccordionItem
372
+ key={concept.id}
373
+ value={concept.id}
374
+ className="standard-table-row"
375
+ >
376
+ <AccordionHeader
377
+ onClick={() => navigateTo("/")}
378
+ className="standard-table-row-header"
379
+ >
380
+ <Icon active={true} className="size-12 shrink-0" />
381
+ <span className="overline-large flex-1">
382
+ {concept.name}
383
+ </span>
384
+ <AccordionTrigger className="standard-table-trigger" />
385
+ </AccordionHeader>
386
+
387
+ <AccordionContent className="standard-table-content">
388
+ <div className="standard-table-content__inner">
389
+ {concept.themes.slice(0, 1).map((theme) => (
390
+ <AccordionListRow
391
+ key={theme.id}
392
+ badge={
393
+ <Tag
394
+ variant="code"
395
+ style={{
396
+ backgroundColor: color.contrast || color.solid,
397
+ borderColor: color.border,
398
+ color: "white",
399
+ }}
400
+ >
401
+ {theme.code}
402
+ </Tag>
403
+ }
404
+ title={theme.name}
405
+ titleClassName="standard-table-list-row__title body-large"
406
+ className="standard-table-list-row standard-table-list-row--nested"
407
+ onClick={() => navigateTo("/")}
408
+ />
409
+ ))}
410
+ </div>
411
+ </AccordionContent>
412
+ </AccordionItem>
413
+ );
414
+ })}
415
+ </Accordion>
416
+ </AccordionContainer>
417
+ </div>
418
+ </div>
419
+ );
420
+ },
421
+ };
422
+
238
423
  export const IWBIThemesTable: Story = {
239
424
  render: () => {
240
425
  const conceptSlug = "community";
@@ -288,6 +473,7 @@ export const IWBIThemesTable: Story = {
288
473
  <AccordionHeader
289
474
  onClick={() => navigateTo("/")}
290
475
  className="standard-table-row-header"
476
+ transparentOnOpen
291
477
  >
292
478
  <Tag
293
479
  variant="code"
@@ -392,6 +578,7 @@ export const IWBIStrategiesTable: Story = {
392
578
  <AccordionHeader
393
579
  onClick={() => navigateTo("/")}
394
580
  className="standard-table-row-header"
581
+ transparentOnOpen
395
582
  >
396
583
  <Tag
397
584
  variant="code"