@ably/ui 17.13.0-dev.d6a24f68 → 17.13.1-dev.5fb54a0a
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/AGENTS.md +337 -0
- package/core/Expander.js +1 -1
- package/core/Expander.js.map +1 -1
- package/core/Flash.js +1 -1
- package/core/Flash.js.map +1 -1
- package/core/Icon/components/icon-display-ephemeral-messages-dark-col.js +2 -0
- package/core/Icon/components/icon-display-ephemeral-messages-dark-col.js.map +1 -0
- package/core/Icon/components/icon-display-message-annotations-dark-col.js +2 -0
- package/core/Icon/components/icon-display-message-annotations-dark-col.js.map +1 -0
- package/core/Icon/components/icon-gui-square-3-stack-3d.js +2 -0
- package/core/Icon/components/icon-gui-square-3-stack-3d.js.map +1 -0
- package/core/Icon/components/index.js +1 -1
- package/core/Icon/components/index.js.map +1 -1
- package/core/Icon/computed-icons/display-icons.js +1 -1
- package/core/Icon/computed-icons/display-icons.js.map +1 -1
- package/core/hooks/use-themed-scrollpoints.js +1 -1
- package/core/hooks/use-themed-scrollpoints.js.map +1 -1
- package/core/icons/display/icon-display-ephemeral-messages-dark-col.svg +6 -0
- package/core/icons/display/icon-display-message-annotations-dark-col.svg +11 -0
- package/core/insights/posthog.js +1 -1
- package/core/insights/posthog.js.map +1 -1
- package/core/remote-blogs-posts.js.map +1 -1
- package/core/remote-session-data.js.map +1 -1
- package/core/scripts.js +1 -1
- package/core/scripts.js.map +1 -1
- package/core/sprites-display.svg +1 -1
- package/index.d.ts +49 -45
- package/package.json +8 -9
- package/core/ConnectStateWrapper.js +0 -2
- package/core/ConnectStateWrapper.js.map +0 -1
- package/core/CookieMessage/component.css +0 -15
- package/core/CookieMessage.js +0 -2
- package/core/CookieMessage.js.map +0 -1
- package/core/remote-data-store.js +0 -2
- package/core/remote-data-store.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -39,6 +39,34 @@ This project is intended to primarily be consumed by the Ably website, voltaire
|
|
|
39
39
|
- **Error Handling**: Wrap external service calls in try-catch, log with logger module
|
|
40
40
|
- **Comments**: JSDoc for props, inline comments for complex logic
|
|
41
41
|
|
|
42
|
+
### Custom Hooks
|
|
43
|
+
|
|
44
|
+
- **Naming**: `use` prefix with descriptive name (e.g., `useContentHeight`, `useThemedScrollpoints`)
|
|
45
|
+
- **JSDoc**: Always include for custom hooks, especially performance-related ones
|
|
46
|
+
- **Parameters**: Document with `@param` including types and defaults
|
|
47
|
+
- **Returns**: Document with `@returns` including type and semantic meaning
|
|
48
|
+
- **Performance rationale**: Include "why" in JSDoc when optimizing (e.g., "eliminates forced reflows")
|
|
49
|
+
- **Cleanup**: Always return cleanup function to prevent memory leaks
|
|
50
|
+
- **Shared constants**: Import from `src/core/utils/heights.ts` instead of duplicating
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
/**
|
|
56
|
+
* Tracks element height using ResizeObserver to avoid forced reflows.
|
|
57
|
+
*
|
|
58
|
+
* @param ref - React ref to the element to observe
|
|
59
|
+
* @param initialHeight - Initial height value (default: 0)
|
|
60
|
+
* @returns Current height in pixels
|
|
61
|
+
*/
|
|
62
|
+
export function useContentHeight(
|
|
63
|
+
ref: RefObject<HTMLElement>,
|
|
64
|
+
initialHeight = 0,
|
|
65
|
+
): number {
|
|
66
|
+
// Implementation...
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
42
70
|
Keep emojis in the code to a minimum, only introduce them if there is precedent
|
|
43
71
|
in the file you're working on.
|
|
44
72
|
|
|
@@ -51,6 +79,9 @@ this is a given for how we work.
|
|
|
51
79
|
files in CI and don't want preventable failures. `pnpm lint:fix` should also
|
|
52
80
|
apply our formatting rules while trying to fix most things for you
|
|
53
81
|
- Run tests with `pnpm test` after making file changes
|
|
82
|
+
- When testing with Storybook, use Chrome DevTools Performance tab to verify no forced reflows
|
|
83
|
+
- For performance-related changes, compare before/after metrics and include in commit/PR
|
|
84
|
+
- Use Chrome MCP at `http://localhost:6006` (Storybook) or `http://localhost:4000` (Voltaire) for visual verification
|
|
54
85
|
|
|
55
86
|
## Styling Guide
|
|
56
87
|
|
|
@@ -188,6 +219,312 @@ Here's a complete button component demonstrating all patterns:
|
|
|
188
219
|
>
|
|
189
220
|
```
|
|
190
221
|
|
|
222
|
+
## Performance Optimization Guidelines
|
|
223
|
+
|
|
224
|
+
### When to Optimize
|
|
225
|
+
|
|
226
|
+
Optimize when Chrome DevTools Performance profiling shows:
|
|
227
|
+
|
|
228
|
+
- Forced reflows/layouts in event handlers (scroll, resize, input)
|
|
229
|
+
- Long tasks blocking the main thread (>50ms)
|
|
230
|
+
- CPU throttling causing device overheating (especially iOS)
|
|
231
|
+
|
|
232
|
+
Common anti-patterns causing forced reflows:
|
|
233
|
+
|
|
234
|
+
- `getBoundingClientRect()` in scroll/resize handlers
|
|
235
|
+
- `clientHeight/scrollHeight/offsetHeight` reads during interactions
|
|
236
|
+
- Synchronous layout queries followed by style changes
|
|
237
|
+
- DOM queries inside throttled/debounced callbacks
|
|
238
|
+
|
|
239
|
+
### Observer API Patterns
|
|
240
|
+
|
|
241
|
+
#### IntersectionObserver (Scroll Position Detection)
|
|
242
|
+
|
|
243
|
+
Use for detecting when elements enter/exit viewport or cross specific boundaries.
|
|
244
|
+
|
|
245
|
+
**Example:** Header theme changes based on which section is visible
|
|
246
|
+
|
|
247
|
+
**Key patterns:**
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const observerRef = useRef<IntersectionObserver | null>(null);
|
|
251
|
+
const intersectingElementsRef = useRef<Map<string, IntersectionObserverEntry>>(new Map());
|
|
252
|
+
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
const intersectingElements = intersectingElementsRef.current;
|
|
255
|
+
|
|
256
|
+
observerRef.current = new IntersectionObserver(
|
|
257
|
+
(entries) => {
|
|
258
|
+
requestAnimationFrame(() => {
|
|
259
|
+
// Update tracking map
|
|
260
|
+
for (const entry of entries) {
|
|
261
|
+
if (entry.isIntersecting) {
|
|
262
|
+
intersectingElements.set(entry.target.id, entry);
|
|
263
|
+
} else {
|
|
264
|
+
intersectingElements.delete(entry.target.id);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Find best match from ALL intersecting elements
|
|
269
|
+
// (observer only reports changes, not all intersecting)
|
|
270
|
+
let bestMatch = null;
|
|
271
|
+
for (const [id, entry] of intersectingElements) {
|
|
272
|
+
const rect = entry.boundingClientRect ?? entry.target.getBoundingClientRect();
|
|
273
|
+
// Calculate match quality...
|
|
274
|
+
if (isBetterMatch) bestMatch = {...};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Only update state if changed
|
|
278
|
+
if (bestMatch && bestMatch.value !== previousValueRef.current) {
|
|
279
|
+
previousValueRef.current = bestMatch.value;
|
|
280
|
+
setState(bestMatch.value);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
rootMargin: "-64px 0px 0px 0px", // Adjust for fixed header
|
|
286
|
+
threshold: 0,
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Observe elements
|
|
291
|
+
elements.forEach(el => observerRef.current?.observe(el));
|
|
292
|
+
|
|
293
|
+
// CRITICAL: Manual initial state check
|
|
294
|
+
// IntersectionObserver callbacks only fire on CHANGES, not initial observation
|
|
295
|
+
const timeoutId = setTimeout(() => {
|
|
296
|
+
// Check which elements currently intersect
|
|
297
|
+
// Set initial state
|
|
298
|
+
}, 0);
|
|
299
|
+
|
|
300
|
+
return () => {
|
|
301
|
+
clearTimeout(timeoutId);
|
|
302
|
+
observerRef.current?.disconnect();
|
|
303
|
+
observerRef.current = null;
|
|
304
|
+
intersectingElements.clear();
|
|
305
|
+
};
|
|
306
|
+
}, [deps]);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Critical points:**
|
|
310
|
+
|
|
311
|
+
- Observer only reports state CHANGES, not all intersecting elements
|
|
312
|
+
- Use Map to track currently intersecting elements
|
|
313
|
+
- Manual initial check with `setTimeout(..., 0)` required
|
|
314
|
+
- Batch updates with `requestAnimationFrame()`
|
|
315
|
+
- Track previous value to skip redundant setState
|
|
316
|
+
- **Tiebreaker logic**: When multiple elements have equal distances, use array order (earlier in array wins)
|
|
317
|
+
- Clean up timeout, observer, and Map
|
|
318
|
+
|
|
319
|
+
**Tiebreaker pattern:**
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// When distances are equal, use scrollpoints array order
|
|
323
|
+
if (
|
|
324
|
+
!bestMatch ||
|
|
325
|
+
distance < bestMatch.distance ||
|
|
326
|
+
(distance === bestMatch.distance && scrollpointIndex < bestMatch.index)
|
|
327
|
+
) {
|
|
328
|
+
bestMatch = { scrollpoint, distance, index: scrollpointIndex };
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Why this matters:** In Voltaire, both `meganav` (transparent) and `main-theme-dark` (with border) start at position 0, giving identical distances. Without a tiebreaker, the header unpredictably showed the border. Array order ensures `meganav` (listed first) always wins.
|
|
333
|
+
|
|
334
|
+
#### ResizeObserver (Height/Size Tracking)
|
|
335
|
+
|
|
336
|
+
Use for tracking element dimensions without synchronous layout reads.
|
|
337
|
+
|
|
338
|
+
**Example:** Expander content height for expand/collapse animations
|
|
339
|
+
|
|
340
|
+
**Key patterns:**
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const rafIdRef = useRef<number | null>(null);
|
|
344
|
+
const observerRef = useRef<ResizeObserver | null>(null);
|
|
345
|
+
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
let isMounted = true;
|
|
348
|
+
|
|
349
|
+
observerRef.current = new ResizeObserver((entries) => {
|
|
350
|
+
// Cancel any pending RAF to avoid stale updates
|
|
351
|
+
if (rafIdRef.current !== null) {
|
|
352
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
rafIdRef.current = requestAnimationFrame(() => {
|
|
356
|
+
rafIdRef.current = null;
|
|
357
|
+
|
|
358
|
+
// Guard against updates after unmount
|
|
359
|
+
if (!isMounted) return;
|
|
360
|
+
|
|
361
|
+
const entry = entries[0];
|
|
362
|
+
if (entry && entry.contentRect) {
|
|
363
|
+
const newHeight = Math.round(entry.contentRect.height);
|
|
364
|
+
setState(newHeight);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
observerRef.current.observe(element);
|
|
370
|
+
|
|
371
|
+
return () => {
|
|
372
|
+
isMounted = false;
|
|
373
|
+
// Cancel pending RAF to prevent setState after unmount
|
|
374
|
+
if (rafIdRef.current !== null) {
|
|
375
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
376
|
+
rafIdRef.current = null;
|
|
377
|
+
}
|
|
378
|
+
observerRef.current?.disconnect();
|
|
379
|
+
observerRef.current = null;
|
|
380
|
+
};
|
|
381
|
+
}, [ref]);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Critical points:**
|
|
385
|
+
|
|
386
|
+
- Always capture RAF ID and cancel on cleanup
|
|
387
|
+
- Use `isMounted` flag to guard setState calls
|
|
388
|
+
- Cancel pending RAF before scheduling new one
|
|
389
|
+
- ResizeObserver doesn't need initial check (fires immediately on observe)
|
|
390
|
+
- Round numeric values for consistency
|
|
391
|
+
|
|
392
|
+
### Testing Async Hooks
|
|
393
|
+
|
|
394
|
+
#### Setup Pattern
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
describe("useMyHook", () => {
|
|
398
|
+
let originalIntersectionObserver: typeof IntersectionObserver;
|
|
399
|
+
let originalRequestAnimationFrame: typeof requestAnimationFrame;
|
|
400
|
+
|
|
401
|
+
beforeEach(() => {
|
|
402
|
+
vi.useFakeTimers();
|
|
403
|
+
|
|
404
|
+
// CRITICAL: Save originals BEFORE mocking
|
|
405
|
+
originalIntersectionObserver = global.IntersectionObserver;
|
|
406
|
+
originalRequestAnimationFrame = global.requestAnimationFrame;
|
|
407
|
+
|
|
408
|
+
// Mock global APIs
|
|
409
|
+
global.IntersectionObserver = vi.fn((callback) => ({
|
|
410
|
+
observe: vi.fn(),
|
|
411
|
+
disconnect: vi.fn(),
|
|
412
|
+
})) as unknown as typeof IntersectionObserver;
|
|
413
|
+
|
|
414
|
+
global.requestAnimationFrame = vi.fn((cb) => {
|
|
415
|
+
cb(0);
|
|
416
|
+
return 0;
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
afterEach(() => {
|
|
421
|
+
vi.clearAllMocks();
|
|
422
|
+
vi.useRealTimers();
|
|
423
|
+
document.body.innerHTML = "";
|
|
424
|
+
|
|
425
|
+
// CRITICAL: Restore originals to prevent test pollution
|
|
426
|
+
global.IntersectionObserver = originalIntersectionObserver;
|
|
427
|
+
global.requestAnimationFrame = originalRequestAnimationFrame;
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Testing Observer Callbacks
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
it("updates state when observer fires", () => {
|
|
436
|
+
const elem = document.createElement("div");
|
|
437
|
+
elem.id = "test";
|
|
438
|
+
elem.getBoundingClientRect = vi.fn().mockReturnValue({ top: 0, bottom: 200 });
|
|
439
|
+
document.body.appendChild(elem);
|
|
440
|
+
|
|
441
|
+
const { result } = renderHook(() => useMyHook());
|
|
442
|
+
|
|
443
|
+
// Advance timers for initial check
|
|
444
|
+
act(() => {
|
|
445
|
+
vi.runAllTimers();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Simulate observer callback
|
|
449
|
+
act(() => {
|
|
450
|
+
observerCallback(
|
|
451
|
+
[
|
|
452
|
+
{
|
|
453
|
+
target: elem,
|
|
454
|
+
isIntersecting: true,
|
|
455
|
+
boundingClientRect: {
|
|
456
|
+
top: 0,
|
|
457
|
+
bottom: 200,
|
|
458
|
+
left: 0,
|
|
459
|
+
right: 0,
|
|
460
|
+
x: 0,
|
|
461
|
+
y: 0,
|
|
462
|
+
width: 0,
|
|
463
|
+
height: 200,
|
|
464
|
+
},
|
|
465
|
+
} as unknown as IntersectionObserverEntry,
|
|
466
|
+
],
|
|
467
|
+
{} as IntersectionObserver,
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
expect(result.current).toBe("expected-value");
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Key points:**
|
|
476
|
+
|
|
477
|
+
- Mock `getBoundingClientRect` on test elements
|
|
478
|
+
- Provide `boundingClientRect` in IntersectionObserverEntry mocks
|
|
479
|
+
- Wrap timer advances and callback calls in `act()`
|
|
480
|
+
- Test both initial state and subsequent updates
|
|
481
|
+
|
|
482
|
+
### Common Pitfalls Checklist
|
|
483
|
+
|
|
484
|
+
When writing performance-optimized hooks:
|
|
485
|
+
|
|
486
|
+
- [ ] RAF cleanup: Store ID, cancel in cleanup
|
|
487
|
+
- [ ] isMounted guard: Prevent setState after unmount
|
|
488
|
+
- [ ] Initial state check: Manual check for IntersectionObserver
|
|
489
|
+
- [ ] Previous value tracking: Skip redundant setState
|
|
490
|
+
- [ ] Map/Set cleanup: Clear in cleanup function
|
|
491
|
+
- [ ] Test mock restoration: Save originals, restore in afterEach
|
|
492
|
+
- [ ] Console warnings: For missing DOM elements (not errors)
|
|
493
|
+
- [ ] Tiebreaker logic: When multiple candidates have equal scores
|
|
494
|
+
|
|
495
|
+
## Storybook Development
|
|
496
|
+
|
|
497
|
+
### Testing Interactive Components
|
|
498
|
+
|
|
499
|
+
- Use `http://localhost:6006` when developing/testing components
|
|
500
|
+
- Create stories that simulate production patterns (e.g., overlapping scrollpoints like Voltaire)
|
|
501
|
+
- Test edge cases in stories (empty arrays, missing DOM elements, rapid state changes)
|
|
502
|
+
|
|
503
|
+
### Performance Testing in Storybook
|
|
504
|
+
|
|
505
|
+
1. Open Chrome DevTools → Performance tab
|
|
506
|
+
2. Start recording while interacting with component
|
|
507
|
+
3. Search for forced reflow indicators:
|
|
508
|
+
- `getBoundingClientRect`
|
|
509
|
+
- `clientHeight`/`scrollHeight`/`offsetHeight`
|
|
510
|
+
- "Forced reflow" warnings
|
|
511
|
+
4. Measure total time in layout/reflow (should be <5ms for interactions)
|
|
512
|
+
|
|
513
|
+
### Simulating Production Patterns
|
|
514
|
+
|
|
515
|
+
When creating stories for layout-dependent components, replicate real-world scenarios:
|
|
516
|
+
|
|
517
|
+
Example - Overlapping scrollpoints (like Voltaire):
|
|
518
|
+
|
|
519
|
+
```tsx
|
|
520
|
+
<div className="relative">
|
|
521
|
+
<div id="hero" className="absolute top-0 h-32" />
|
|
522
|
+
<div id="main" className="relative pt-32 h-screen" />
|
|
523
|
+
</div>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
This allows testing tiebreaker logic and initial state detection. Storybook stories should replicate production layout patterns to catch bugs like the tiebreaker issue. The original simple sequential zones didn't expose the Voltaire bug where elements start at the same position.
|
|
527
|
+
|
|
191
528
|
## Git workflow
|
|
192
529
|
|
|
193
530
|
- Always do work on a new branch, start the branch on the HEAD of `origin/main`
|
package/core/Expander.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React,{useMemo,useRef,useState}from"react";import*as RadixCollapsible from"@radix-ui/react-collapsible";import cn from"./utils/cn";import{useContentHeight}from"./hooks/use-content-height";const Expander=({heightThreshold=200,className,fadeClassName,controlsClassName,controlsOpenedLabel,controlsClosedLabel,children})=>{const innerRef=useRef(null);const[expanded,setExpanded]=useState(false);const contentHeight=useContentHeight(innerRef,heightThreshold);const showControls=useMemo(()=>contentHeight>=heightThreshold,[contentHeight,heightThreshold]);const height=useMemo(()=>contentHeight<heightThreshold?"auto":expanded?contentHeight:heightThreshold,[contentHeight,heightThreshold,expanded]);return React.createElement(RadixCollapsible.Root,{open:expanded,onOpenChange:setExpanded},React.createElement("div",{style:{height},"data-testid":"expander-container",className:cn("overflow-hidden transition-all relative",className)},showControls&&!expanded&&React.createElement("div",{className:cn("h-16 w-full bg-gradient-to-t from-white to-transparent absolute bottom-0 left-0 right-0",fadeClassName)}),React.createElement("div",{ref:innerRef},children)),showControls&&React.createElement(RadixCollapsible.Trigger,{asChild:true},React.createElement("button",{"data-testid":"expander-controls",className:cn(heightThreshold===0&&!expanded?"":"mt-4","cursor-pointer font-bold text-gui-blue-default-light hover:text-gui-blue-hover-light",controlsClassName)},expanded?controlsOpenedLabel??"View less -":controlsClosedLabel??"View all +")))};export default Expander;
|
|
1
|
+
import React,{useMemo,useRef,useState}from"react";import*as RadixCollapsible from"@radix-ui/react-collapsible";import cn from"./utils/cn";import{useContentHeight}from"./hooks/use-content-height";const Expander=({heightThreshold=200,className,fadeClassName,controlsClassName,controlsOpenedLabel,controlsClosedLabel,children})=>{const innerRef=useRef(null);const[expanded,setExpanded]=useState(false);const contentHeight=useContentHeight(innerRef,heightThreshold);const showControls=useMemo(()=>contentHeight>=heightThreshold,[contentHeight,heightThreshold]);const height=useMemo(()=>contentHeight<heightThreshold?"auto":expanded?contentHeight:heightThreshold,[contentHeight,heightThreshold,expanded]);return React.createElement(RadixCollapsible.Root,{open:expanded,onOpenChange:setExpanded},React.createElement("div",{style:{height},"data-testid":"expander-container",className:cn("overflow-hidden transition-all relative",className)},showControls&&!expanded&&React.createElement("div",{className:cn("h-16 w-full bg-gradient-to-t from-white to-transparent absolute bottom-0 left-0 right-0",fadeClassName)}),React.createElement("div",{ref:innerRef},children)),showControls&&React.createElement(RadixCollapsible.Trigger,{asChild:true},React.createElement("button",{"data-testid":"expander-controls",className:cn(heightThreshold===0&&!expanded?"":"mt-4","cursor-pointer font-bold text-gui-blue-default-light hover:text-gui-blue-hover-light focus-base transition-colors",controlsClassName)},expanded?controlsOpenedLabel??"View less -":controlsClosedLabel??"View all +")))};export default Expander;
|
|
2
2
|
//# sourceMappingURL=Expander.js.map
|
package/core/Expander.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/Expander.tsx"],"sourcesContent":["import React, {\n PropsWithChildren,\n ReactNode,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport * as RadixCollapsible from \"@radix-ui/react-collapsible\";\nimport cn from \"./utils/cn\";\nimport { useContentHeight } from \"./hooks/use-content-height\";\n\ntype ExpanderProps = {\n heightThreshold?: number;\n className?: string;\n fadeClassName?: string;\n controlsClassName?: string;\n controlsOpenedLabel?: string | ReactNode;\n controlsClosedLabel?: string | ReactNode;\n};\n\nconst Expander = ({\n heightThreshold = 200,\n className,\n fadeClassName,\n controlsClassName,\n controlsOpenedLabel,\n controlsClosedLabel,\n children,\n}: PropsWithChildren<ExpanderProps>) => {\n const innerRef = useRef<HTMLDivElement>(null);\n const [expanded, setExpanded] = useState(false);\n\n const contentHeight = useContentHeight(innerRef, heightThreshold);\n\n const showControls = useMemo(\n () => contentHeight >= heightThreshold,\n [contentHeight, heightThreshold],\n );\n\n const height = useMemo(\n () =>\n contentHeight < heightThreshold\n ? \"auto\"\n : expanded\n ? contentHeight\n : heightThreshold,\n [contentHeight, heightThreshold, expanded],\n );\n\n return (\n <RadixCollapsible.Root open={expanded} onOpenChange={setExpanded}>\n <div\n style={{ height }}\n data-testid=\"expander-container\"\n className={cn(\"overflow-hidden transition-all relative\", className)}\n >\n {showControls && !expanded && (\n <div\n className={cn(\n \"h-16 w-full bg-gradient-to-t from-white to-transparent absolute bottom-0 left-0 right-0\",\n fadeClassName,\n )}\n ></div>\n )}\n <div ref={innerRef}>{children}</div>\n </div>\n {showControls && (\n <RadixCollapsible.Trigger asChild>\n <button\n data-testid=\"expander-controls\"\n className={cn(\n heightThreshold === 0 && !expanded ? \"\" : \"mt-4\",\n \"cursor-pointer font-bold text-gui-blue-default-light hover:text-gui-blue-hover-light\",\n controlsClassName,\n )}\n >\n {expanded\n ? (controlsOpenedLabel ?? \"View less -\")\n : (controlsClosedLabel ?? \"View all +\")}\n </button>\n </RadixCollapsible.Trigger>\n )}\n </RadixCollapsible.Root>\n );\n};\n\nexport default Expander;\n"],"names":["React","useMemo","useRef","useState","RadixCollapsible","cn","useContentHeight","Expander","heightThreshold","className","fadeClassName","controlsClassName","controlsOpenedLabel","controlsClosedLabel","children","innerRef","expanded","setExpanded","contentHeight","showControls","height","Root","open","onOpenChange","div","style","data-testid","ref","Trigger","asChild","button"],"mappings":"AAAA,OAAOA,OAGLC,OAAO,CACPC,MAAM,CACNC,QAAQ,KACH,OAAQ,AACf,WAAYC,qBAAsB,6BAA8B,AAChE,QAAOC,OAAQ,YAAa,AAC5B,QAASC,gBAAgB,KAAQ,4BAA6B,CAW9D,MAAMC,SAAW,CAAC,CAChBC,gBAAkB,GAAG,CACrBC,SAAS,CACTC,aAAa,CACbC,iBAAiB,CACjBC,mBAAmB,CACnBC,mBAAmB,CACnBC,QAAQ,CACyB,IACjC,MAAMC,SAAWb,OAAuB,MACxC,KAAM,CAACc,SAAUC,YAAY,CAAGd,SAAS,OAEzC,MAAMe,cAAgBZ,iBAAiBS,SAAUP,iBAEjD,MAAMW,aAAelB,QACnB,IAAMiB,eAAiBV,gBACvB,CAACU,cAAeV,gBAAgB,EAGlC,MAAMY,OAASnB,QACb,IACEiB,cAAgBV,gBACZ,OACAQ,SACEE,cACAV,gBACR,CAACU,cAAeV,gBAAiBQ,SAAS,EAG5C,OACE,oBAACZ,iBAAiBiB,IAAI,EAACC,KAAMN,SAAUO,aAAcN,aACnD,oBAACO,OACCC,MAAO,CAAEL,MAAO,EAChBM,cAAY,qBACZjB,UAAWJ,GAAG,0CAA2CI,YAExDU,cAAgB,CAACH,UAChB,oBAACQ,OACCf,UAAWJ,GACT,0FACAK,iBAIN,oBAACc,OAAIG,IAAKZ,UAAWD,WAEtBK,cACC,oBAACf,iBAAiBwB,OAAO,EAACC,QAAAA,MACxB,oBAACC,UACCJ,cAAY,oBACZjB,UAAWJ,GACTG,kBAAoB,GAAK,CAACQ,SAAW,GAAK,OAC1C,
|
|
1
|
+
{"version":3,"sources":["../../src/core/Expander.tsx"],"sourcesContent":["import React, {\n PropsWithChildren,\n ReactNode,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport * as RadixCollapsible from \"@radix-ui/react-collapsible\";\nimport cn from \"./utils/cn\";\nimport { useContentHeight } from \"./hooks/use-content-height\";\n\ntype ExpanderProps = {\n heightThreshold?: number;\n className?: string;\n fadeClassName?: string;\n controlsClassName?: string;\n controlsOpenedLabel?: string | ReactNode;\n controlsClosedLabel?: string | ReactNode;\n};\n\nconst Expander = ({\n heightThreshold = 200,\n className,\n fadeClassName,\n controlsClassName,\n controlsOpenedLabel,\n controlsClosedLabel,\n children,\n}: PropsWithChildren<ExpanderProps>) => {\n const innerRef = useRef<HTMLDivElement>(null);\n const [expanded, setExpanded] = useState(false);\n\n const contentHeight = useContentHeight(innerRef, heightThreshold);\n\n const showControls = useMemo(\n () => contentHeight >= heightThreshold,\n [contentHeight, heightThreshold],\n );\n\n const height = useMemo(\n () =>\n contentHeight < heightThreshold\n ? \"auto\"\n : expanded\n ? contentHeight\n : heightThreshold,\n [contentHeight, heightThreshold, expanded],\n );\n\n return (\n <RadixCollapsible.Root open={expanded} onOpenChange={setExpanded}>\n <div\n style={{ height }}\n data-testid=\"expander-container\"\n className={cn(\"overflow-hidden transition-all relative\", className)}\n >\n {showControls && !expanded && (\n <div\n className={cn(\n \"h-16 w-full bg-gradient-to-t from-white to-transparent absolute bottom-0 left-0 right-0\",\n fadeClassName,\n )}\n ></div>\n )}\n <div ref={innerRef}>{children}</div>\n </div>\n {showControls && (\n <RadixCollapsible.Trigger asChild>\n <button\n data-testid=\"expander-controls\"\n className={cn(\n heightThreshold === 0 && !expanded ? \"\" : \"mt-4\",\n \"cursor-pointer font-bold text-gui-blue-default-light hover:text-gui-blue-hover-light focus-base transition-colors\",\n controlsClassName,\n )}\n >\n {expanded\n ? (controlsOpenedLabel ?? \"View less -\")\n : (controlsClosedLabel ?? \"View all +\")}\n </button>\n </RadixCollapsible.Trigger>\n )}\n </RadixCollapsible.Root>\n );\n};\n\nexport default Expander;\n"],"names":["React","useMemo","useRef","useState","RadixCollapsible","cn","useContentHeight","Expander","heightThreshold","className","fadeClassName","controlsClassName","controlsOpenedLabel","controlsClosedLabel","children","innerRef","expanded","setExpanded","contentHeight","showControls","height","Root","open","onOpenChange","div","style","data-testid","ref","Trigger","asChild","button"],"mappings":"AAAA,OAAOA,OAGLC,OAAO,CACPC,MAAM,CACNC,QAAQ,KACH,OAAQ,AACf,WAAYC,qBAAsB,6BAA8B,AAChE,QAAOC,OAAQ,YAAa,AAC5B,QAASC,gBAAgB,KAAQ,4BAA6B,CAW9D,MAAMC,SAAW,CAAC,CAChBC,gBAAkB,GAAG,CACrBC,SAAS,CACTC,aAAa,CACbC,iBAAiB,CACjBC,mBAAmB,CACnBC,mBAAmB,CACnBC,QAAQ,CACyB,IACjC,MAAMC,SAAWb,OAAuB,MACxC,KAAM,CAACc,SAAUC,YAAY,CAAGd,SAAS,OAEzC,MAAMe,cAAgBZ,iBAAiBS,SAAUP,iBAEjD,MAAMW,aAAelB,QACnB,IAAMiB,eAAiBV,gBACvB,CAACU,cAAeV,gBAAgB,EAGlC,MAAMY,OAASnB,QACb,IACEiB,cAAgBV,gBACZ,OACAQ,SACEE,cACAV,gBACR,CAACU,cAAeV,gBAAiBQ,SAAS,EAG5C,OACE,oBAACZ,iBAAiBiB,IAAI,EAACC,KAAMN,SAAUO,aAAcN,aACnD,oBAACO,OACCC,MAAO,CAAEL,MAAO,EAChBM,cAAY,qBACZjB,UAAWJ,GAAG,0CAA2CI,YAExDU,cAAgB,CAACH,UAChB,oBAACQ,OACCf,UAAWJ,GACT,0FACAK,iBAIN,oBAACc,OAAIG,IAAKZ,UAAWD,WAEtBK,cACC,oBAACf,iBAAiBwB,OAAO,EAACC,QAAAA,MACxB,oBAACC,UACCJ,cAAY,oBACZjB,UAAWJ,GACTG,kBAAoB,GAAK,CAACQ,SAAW,GAAK,OAC1C,oHACAL,oBAGDK,SACIJ,qBAAuB,cACvBC,qBAAuB,eAMxC,CAEA,gBAAeN,QAAS"}
|
package/core/Flash.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React,{useEffect,useState,useRef}from"react";import DOMPurify from"dompurify";import
|
|
1
|
+
import React,{useEffect,useState,useRef,createContext,useContext,useCallback,useMemo}from"react";import DOMPurify from"dompurify";import Icon from"./Icon";import cn from"./utils/cn";const FLASH_DATA_ID="ui-flashes";const FlashContext=createContext(undefined);const FlashProvider=({children})=>{const[flashes,setFlashes]=useState([]);const removeFlash=useCallback(flashId=>{setFlashes(prev=>prev.filter(item=>item.id!==flashId))},[]);const addFlashes=useCallback(newFlashes=>{setFlashes(prev=>{const withIds=newFlashes.filter(flash=>!prev.some(existing=>existing.content===flash.content&&existing.type===flash.type)).map(flash=>({...flash,id:Math.random().toString(36).slice(2),removed:false,removeFlash}));return[...prev,...withIds]})},[removeFlash]);const contextValue=useMemo(()=>({flashes,addFlashes,removeFlash}),[flashes,addFlashes,removeFlash]);return React.createElement(FlashContext.Provider,{value:contextValue},children)};const useFlashContext=()=>{const context=useContext(FlashContext);if(context===undefined){throw new Error("useFlashContext must be used within FlashProvider")}return context};const FLASH_BG_COLOR={error:"bg-gui-error",success:"bg-green-400",notice:"bg-blue-400",info:"bg-blue-400",alert:"bg-orange-600"};const FLASH_TEXT_COLOR={error:"text-neutral-000",success:"text-neutral-1300",notice:"text-neutral-1300",info:"text-neutral-1300",alert:"text-neutral-000"};const AUTO_HIDE=["success","info","notice"];const AUTO_HIDE_TIME=8e3;const useAutoHide=(type,closeFlash)=>{const timeoutId=useRef(null);useEffect(()=>{if(AUTO_HIDE.includes(type)){timeoutId.current=setTimeout(()=>{closeFlash()},AUTO_HIDE_TIME)}return()=>{if(timeoutId.current){clearTimeout(timeoutId.current)}}},[type,closeFlash])};const Flash=({id,type,content,removeFlash})=>{const ref=useRef(null);const[closed,setClosed]=useState(false);const[flashHeight,setFlashHeight]=useState(0);const closeFlash=()=>{if(ref.current){setFlashHeight(ref.current.getBoundingClientRect().height)}setClosed(true);setTimeout(()=>{if(id){removeFlash(id)}},100)};useAutoHide(type,closeFlash);const animateEntry=!closed;let style;if(flashHeight&&!closed){style={height:`${flashHeight}px`}}else if(closed){style={height:0,marginTop:0,zIndex:-1}}else{style={}}const safeContent=DOMPurify.sanitize(content,{ALLOWED_TAGS:["a"],ALLOWED_ATTR:["href","data-method"],ALLOWED_URI_REGEXP:/^\/[^/]/});const withIcons={notice:"icon-gui-ably-badge",success:"icon-gui-check-outline",error:"icon-gui-exclamation-triangle-outline",alert:"icon-gui-exclamation-triangle-outline",info:""};const iconColor={notice:FLASH_TEXT_COLOR.notice,success:FLASH_TEXT_COLOR.success,error:FLASH_TEXT_COLOR.error,alert:FLASH_TEXT_COLOR.alert,info:""};return React.createElement("div",{className:cn("ui-flash-message ui-grid-px",animateEntry&&"ui-flash-message-enter"),style:style,ref:ref,"data-id":"ui-flash","data-testid":"ui-flash"},React.createElement("div",{className:cn(FLASH_BG_COLOR[type],"p-8 flex align-center rounded shadow-container-subtle")},withIcons[type]&&iconColor[type]&&React.createElement(Icon,{name:withIcons[type],color:iconColor[type],size:"1.5rem",additionalCSS:"mr-4 self-baseline"}),React.createElement("p",{className:cn("ui-text-p1 mr-4",FLASH_TEXT_COLOR[type]),dangerouslySetInnerHTML:{__html:safeContent}}),React.createElement("button",{type:"button",className:"p-0 ml-auto self-start focus:outline-none",onClick:closeFlash},iconColor[type]&&React.createElement(Icon,{name:"icon-gui-x-mark-outline",color:iconColor[type],size:"1.5rem",additionalCSS:"transition-colors"}))))};const Flashes=()=>{const{flashes,removeFlash}=useFlashContext();return React.createElement("div",{className:"ui-flash","data-id":FLASH_DATA_ID},flashes.filter(item=>!item.removed).map(flash=>React.createElement(Flash,{key:flash.id,...flash,removeFlash:removeFlash})))};const BackendFlashes=({flashes})=>{const context=useContext(FlashContext);useEffect(()=>{if(!context){console.warn("BackendFlashes must be used within FlashProvider");return}const transformedFlashes=flashes.map(flash=>{const[type,content]=flash;return{type:type,content}});if(transformedFlashes.length>0){context.addFlashes(transformedFlashes)}},[flashes]);if(!context)return null;return React.createElement(Flashes,null)};export{FLASH_DATA_ID,Flashes,FlashProvider,useFlashContext};export default BackendFlashes;
|
|
2
2
|
//# sourceMappingURL=Flash.js.map
|
package/core/Flash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/Flash.tsx"],"sourcesContent":["import React, { useEffect, useState, useRef } from \"react\";\nimport DOMPurify from \"dompurify\";\nimport { getRemoteDataStore } from \"./remote-data-store.js\";\nimport ConnectStateWrapper from \"./ConnectStateWrapper\";\nimport Icon from \"./Icon\";\nimport { ColorClass } from \"./styles/colors/types\";\nimport { IconName } from \"./Icon/types\";\n\ntype FlashPropsType = \"error\" | \"success\" | \"notice\" | \"info\" | \"alert\";\n\ntype FlashProps = {\n id: string;\n removed: boolean;\n type: FlashPropsType;\n content: string;\n removeFlash: (id: string) => void;\n};\n\ntype FlashesProps = {\n flashes: { items: Pick<FlashProps, \"type\" | \"content\">[] };\n};\n\ntype BackendFlashesProps = {\n flashes: string[][];\n};\n\nconst REDUCER_KEY = \"flashes\";\nconst FLASH_DATA_ID = \"ui-flashes\";\n\nconst initialState = { items: [] };\n\nconst reducerFlashes = {\n [REDUCER_KEY]: (\n state: {\n items: FlashProps[];\n } = initialState,\n action: { type: string; payload: FlashProps | FlashProps[] },\n ) => {\n switch (action.type) {\n case \"flash/push\": {\n const flashes = Array.isArray(action.payload)\n ? action.payload\n : [action.payload];\n return { items: [...state.items, ...flashes] };\n }\n default:\n return state;\n }\n },\n};\n\n// Not cool but redux isn't a long term plan here\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst selectFlashes = (store: any): { items: FlashProps[] } =>\n store.getState()[REDUCER_KEY];\n\nconst FLASH_BG_COLOR = {\n error: \"bg-gui-error\",\n success: \"bg-zingy-green\",\n notice: \"bg-electric-cyan\",\n info: \"bg-electric-cyan\",\n alert: \"bg-active-orange\",\n};\n\nconst FLASH_TEXT_COLOR = {\n error: \"text-white\",\n success: \"text-cool-black\",\n notice: \"text-cool-black\",\n info: \"text-cool-black\",\n alert: \"text-white\",\n};\n\nconst AUTO_HIDE = [\"success\", \"info\", \"notice\"];\nconst AUTO_HIDE_TIME = 8000;\n\nconst useAutoHide = (type: string, closeFlash: () => void) => {\n const timeoutId = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (AUTO_HIDE.includes(type)) {\n timeoutId.current = setTimeout(() => {\n closeFlash();\n }, AUTO_HIDE_TIME);\n }\n\n return () => {\n if (timeoutId.current) {\n clearTimeout(timeoutId.current);\n }\n };\n }, [type, closeFlash]);\n};\n\nconst Flash = ({ id, type, content, removeFlash }: FlashProps) => {\n const ref = useRef<HTMLDivElement>(null);\n const [closed, setClosed] = useState(false);\n const [flashHeight, setFlashHeight] = useState(0);\n\n const closeFlash = () => {\n if (ref.current) {\n setFlashHeight(ref.current.getBoundingClientRect().height);\n }\n\n setClosed(true);\n\n setTimeout(() => {\n if (id) {\n removeFlash(id);\n }\n }, 100);\n };\n\n useAutoHide(type, closeFlash);\n\n const animateEntry = !closed;\n\n let style;\n\n if (flashHeight && !closed) {\n style = { height: `${flashHeight}px` };\n } else if (closed) {\n style = { height: 0, marginTop: 0, zIndex: -1 };\n } else {\n style = {};\n }\n\n const safeContent = DOMPurify.sanitize(content, {\n ALLOWED_TAGS: [\"a\"],\n ALLOWED_ATTR: [\"href\", \"data-method\"],\n ALLOWED_URI_REGEXP: /^\\/[^/]/,\n });\n\n const withIcons: Record<FlashPropsType, IconName | \"\"> = {\n notice: \"icon-gui-ably-badge\",\n success: \"icon-gui-check-outline\",\n error: \"icon-gui-exclamation-triangle-outline\",\n alert: \"icon-gui-exclamation-triangle-outline\",\n info: \"\",\n };\n\n const iconColor: Record<FlashPropsType, ColorClass | \"\"> = {\n notice: \"text-cool-black\",\n success: \"text-cool-black\",\n error: \"text-white\",\n alert: \"text-white\",\n info: \"\",\n };\n\n return (\n <div\n className={`ui-flash-message ui-grid-px ${\n animateEntry ? \"ui-flash-message-enter\" : \"\"\n }`}\n style={style}\n ref={ref}\n data-id=\"ui-flash\"\n data-testid=\"ui-flash\"\n >\n <div\n className={`${FLASH_BG_COLOR[type]} p-8 flex align-center rounded shadow-container-subtle`}\n >\n {withIcons[type] && iconColor[type] && (\n <Icon\n name={withIcons[type]}\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"mr-4 self-baseline\"\n />\n )}\n <p\n className={`ui-text-p1 mr-4 ${FLASH_TEXT_COLOR[type]}`}\n dangerouslySetInnerHTML={{ __html: safeContent }}\n />\n <button\n type=\"button\"\n className=\"p-0 ml-auto self-start focus:outline-none\"\n onClick={closeFlash}\n >\n {iconColor[type] && (\n <Icon\n name=\"icon-gui-x-mark-outline\"\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"transition-colors\"\n />\n )}\n </button>\n </div>\n </div>\n );\n};\n\nconst Flashes = ({ flashes }: FlashesProps) => {\n const [flashesWithIds, setFlashesWithIds] = useState<FlashProps[]>([]);\n\n const removeFlash = (flashId: string) =>\n setFlashesWithIds((items) => items.filter((item) => item.id !== flashId));\n\n useEffect(() => {\n if (!flashes?.items?.length) return;\n\n setFlashesWithIds((state) => {\n const newFlashes = (flashes.items ?? [])\n .filter(\n (flash) =>\n !state.some(\n (existing) =>\n existing.content === flash.content &&\n existing.type === flash.type,\n ),\n )\n .map((flash) => ({\n ...flash,\n id: Math.random().toString(36).slice(2),\n removed: false,\n removeFlash,\n }));\n\n return newFlashes.length > 0 ? [...state, ...newFlashes] : state;\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [flashes]);\n\n return (\n <div className=\"ui-flash\" data-id={FLASH_DATA_ID}>\n {flashesWithIds\n .filter((item) => !item.removed)\n .map((flash) => (\n <Flash key={flash.id} {...flash} />\n ))}\n </div>\n );\n};\n\nconst BackendFlashes = ({ flashes }: BackendFlashesProps) => {\n useEffect(() => {\n const transformedFlashes =\n flashes.map((flash) => {\n const [type, content] = flash;\n return { type, content };\n }) || [];\n\n if (transformedFlashes.length > 0) {\n const store = getRemoteDataStore();\n\n store.dispatch({\n type: \"flash/push\",\n payload: transformedFlashes,\n });\n }\n }, [flashes]);\n\n const WrappedFlashes = ConnectStateWrapper(Flashes, {\n flashes: selectFlashes,\n });\n\n return <WrappedFlashes />;\n};\n\nexport { reducerFlashes, FLASH_DATA_ID, Flashes };\nexport default BackendFlashes;\n"],"names":["React","useEffect","useState","useRef","DOMPurify","getRemoteDataStore","ConnectStateWrapper","Icon","REDUCER_KEY","FLASH_DATA_ID","initialState","items","reducerFlashes","state","action","type","flashes","Array","isArray","payload","selectFlashes","store","getState","FLASH_BG_COLOR","error","success","notice","info","alert","FLASH_TEXT_COLOR","AUTO_HIDE","AUTO_HIDE_TIME","useAutoHide","closeFlash","timeoutId","includes","current","setTimeout","clearTimeout","Flash","id","content","removeFlash","ref","closed","setClosed","flashHeight","setFlashHeight","getBoundingClientRect","height","animateEntry","style","marginTop","zIndex","safeContent","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","ALLOWED_URI_REGEXP","withIcons","iconColor","div","className","data-id","data-testid","name","color","size","additionalCSS","p","dangerouslySetInnerHTML","__html","button","onClick","Flashes","flashesWithIds","setFlashesWithIds","flashId","filter","item","length","newFlashes","flash","some","existing","map","Math","random","toString","slice","removed","key","BackendFlashes","transformedFlashes","dispatch","WrappedFlashes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/Flash.tsx"],"sourcesContent":["import React, {\n useEffect,\n useState,\n useRef,\n createContext,\n useContext,\n useCallback,\n useMemo,\n PropsWithChildren,\n} from \"react\";\nimport DOMPurify from \"dompurify\";\nimport Icon from \"./Icon\";\nimport { ColorClass } from \"./styles/colors/types\";\nimport { IconName } from \"./Icon/types\";\nimport cn from \"./utils/cn\";\n\ntype FlashPropsType = \"error\" | \"success\" | \"notice\" | \"info\" | \"alert\";\n\ntype FlashProps = {\n id: string;\n removed: boolean;\n type: FlashPropsType;\n content: string;\n removeFlash: (id: string) => void;\n};\n\ntype BackendFlashesProps = {\n flashes: string[][];\n};\n\nconst FLASH_DATA_ID = \"ui-flashes\";\n\ntype FlashContextType = {\n flashes: FlashProps[];\n addFlashes: (flashes: Pick<FlashProps, \"type\" | \"content\">[]) => void;\n removeFlash: (id: string) => void;\n};\n\nconst FlashContext = createContext<FlashContextType | undefined>(undefined);\n\ntype FlashProviderProps = PropsWithChildren;\n\nconst FlashProvider = ({ children }: FlashProviderProps) => {\n const [flashes, setFlashes] = useState<FlashProps[]>([]);\n\n const removeFlash = useCallback((flashId: string) => {\n setFlashes((prev) => prev.filter((item) => item.id !== flashId));\n }, []);\n\n const addFlashes = useCallback(\n (newFlashes: Pick<FlashProps, \"type\" | \"content\">[]) => {\n setFlashes((prev) => {\n const withIds = newFlashes\n .filter(\n (flash) =>\n !prev.some(\n (existing) =>\n existing.content === flash.content &&\n existing.type === flash.type,\n ),\n )\n .map((flash) => ({\n ...flash,\n id: Math.random().toString(36).slice(2),\n removed: false,\n removeFlash,\n }));\n\n return [...prev, ...withIds];\n });\n },\n [removeFlash],\n );\n\n const contextValue = useMemo(\n () => ({ flashes, addFlashes, removeFlash }),\n [flashes, addFlashes, removeFlash],\n );\n\n return (\n <FlashContext.Provider value={contextValue}>\n {children}\n </FlashContext.Provider>\n );\n};\n\nconst useFlashContext = () => {\n const context = useContext(FlashContext);\n if (context === undefined) {\n throw new Error(\"useFlashContext must be used within FlashProvider\");\n }\n return context;\n};\n\nconst FLASH_BG_COLOR = {\n error: \"bg-gui-error\",\n success: \"bg-green-400\",\n notice: \"bg-blue-400\",\n info: \"bg-blue-400\",\n alert: \"bg-orange-600\",\n};\n\nconst FLASH_TEXT_COLOR = {\n error: \"text-neutral-000\",\n success: \"text-neutral-1300\",\n notice: \"text-neutral-1300\",\n info: \"text-neutral-1300\",\n alert: \"text-neutral-000\",\n};\n\nconst AUTO_HIDE = [\"success\", \"info\", \"notice\"];\nconst AUTO_HIDE_TIME = 8000;\n\nconst useAutoHide = (type: string, closeFlash: () => void) => {\n const timeoutId = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (AUTO_HIDE.includes(type)) {\n timeoutId.current = setTimeout(() => {\n closeFlash();\n }, AUTO_HIDE_TIME);\n }\n\n return () => {\n if (timeoutId.current) {\n clearTimeout(timeoutId.current);\n }\n };\n }, [type, closeFlash]);\n};\n\nconst Flash = ({ id, type, content, removeFlash }: FlashProps) => {\n const ref = useRef<HTMLDivElement>(null);\n const [closed, setClosed] = useState(false);\n const [flashHeight, setFlashHeight] = useState(0);\n\n const closeFlash = () => {\n if (ref.current) {\n setFlashHeight(ref.current.getBoundingClientRect().height);\n }\n\n setClosed(true);\n\n setTimeout(() => {\n if (id) {\n removeFlash(id);\n }\n }, 100);\n };\n\n useAutoHide(type, closeFlash);\n\n const animateEntry = !closed;\n\n let style;\n\n if (flashHeight && !closed) {\n style = { height: `${flashHeight}px` };\n } else if (closed) {\n style = { height: 0, marginTop: 0, zIndex: -1 };\n } else {\n style = {};\n }\n\n const safeContent = DOMPurify.sanitize(content, {\n ALLOWED_TAGS: [\"a\"],\n ALLOWED_ATTR: [\"href\", \"data-method\"],\n ALLOWED_URI_REGEXP: /^\\/[^/]/,\n });\n\n const withIcons: Record<FlashPropsType, IconName | \"\"> = {\n notice: \"icon-gui-ably-badge\",\n success: \"icon-gui-check-outline\",\n error: \"icon-gui-exclamation-triangle-outline\",\n alert: \"icon-gui-exclamation-triangle-outline\",\n info: \"\",\n };\n\n const iconColor: Record<FlashPropsType, ColorClass | \"\"> = {\n notice: FLASH_TEXT_COLOR.notice as ColorClass,\n success: FLASH_TEXT_COLOR.success as ColorClass,\n error: FLASH_TEXT_COLOR.error as ColorClass,\n alert: FLASH_TEXT_COLOR.alert as ColorClass,\n info: \"\",\n };\n\n return (\n <div\n className={cn(\n \"ui-flash-message ui-grid-px\",\n animateEntry && \"ui-flash-message-enter\",\n )}\n style={style}\n ref={ref}\n data-id=\"ui-flash\"\n data-testid=\"ui-flash\"\n >\n <div\n className={cn(\n FLASH_BG_COLOR[type],\n \"p-8 flex align-center rounded shadow-container-subtle\",\n )}\n >\n {withIcons[type] && iconColor[type] && (\n <Icon\n name={withIcons[type]}\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"mr-4 self-baseline\"\n />\n )}\n <p\n className={cn(\"ui-text-p1 mr-4\", FLASH_TEXT_COLOR[type])}\n dangerouslySetInnerHTML={{ __html: safeContent }}\n />\n <button\n type=\"button\"\n className=\"p-0 ml-auto self-start focus:outline-none\"\n onClick={closeFlash}\n >\n {iconColor[type] && (\n <Icon\n name=\"icon-gui-x-mark-outline\"\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"transition-colors\"\n />\n )}\n </button>\n </div>\n </div>\n );\n};\n\nconst Flashes = () => {\n const { flashes, removeFlash } = useFlashContext();\n\n return (\n <div className=\"ui-flash\" data-id={FLASH_DATA_ID}>\n {flashes\n .filter((item) => !item.removed)\n .map((flash) => (\n <Flash key={flash.id} {...flash} removeFlash={removeFlash} />\n ))}\n </div>\n );\n};\n\nconst BackendFlashes = ({ flashes }: BackendFlashesProps) => {\n const context = useContext(FlashContext);\n\n useEffect(() => {\n if (!context) {\n console.warn(\"BackendFlashes must be used within FlashProvider\");\n return;\n }\n\n const transformedFlashes = flashes.map((flash) => {\n const [type, content] = flash;\n return { type: type as FlashPropsType, content };\n });\n\n if (transformedFlashes.length > 0) {\n context.addFlashes(transformedFlashes);\n }\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [flashes]);\n\n if (!context) return null;\n\n return <Flashes />;\n};\n\nexport { FLASH_DATA_ID, Flashes, FlashProvider, useFlashContext };\nexport default BackendFlashes;\n"],"names":["React","useEffect","useState","useRef","createContext","useContext","useCallback","useMemo","DOMPurify","Icon","cn","FLASH_DATA_ID","FlashContext","undefined","FlashProvider","children","flashes","setFlashes","removeFlash","flashId","prev","filter","item","id","addFlashes","newFlashes","withIds","flash","some","existing","content","type","map","Math","random","toString","slice","removed","contextValue","Provider","value","useFlashContext","context","Error","FLASH_BG_COLOR","error","success","notice","info","alert","FLASH_TEXT_COLOR","AUTO_HIDE","AUTO_HIDE_TIME","useAutoHide","closeFlash","timeoutId","includes","current","setTimeout","clearTimeout","Flash","ref","closed","setClosed","flashHeight","setFlashHeight","getBoundingClientRect","height","animateEntry","style","marginTop","zIndex","safeContent","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","ALLOWED_URI_REGEXP","withIcons","iconColor","div","className","data-id","data-testid","name","color","size","additionalCSS","p","dangerouslySetInnerHTML","__html","button","onClick","Flashes","key","BackendFlashes","console","warn","transformedFlashes","length"],"mappings":"AAAA,OAAOA,OACLC,SAAS,CACTC,QAAQ,CACRC,MAAM,CACNC,aAAa,CACbC,UAAU,CACVC,WAAW,CACXC,OAAO,KAEF,OAAQ,AACf,QAAOC,cAAe,WAAY,AAClC,QAAOC,SAAU,QAAS,AAG1B,QAAOC,OAAQ,YAAa,CAgB5B,MAAMC,cAAgB,aAQtB,MAAMC,aAAeR,cAA4CS,WAIjE,MAAMC,cAAgB,CAAC,CAAEC,QAAQ,CAAsB,IACrD,KAAM,CAACC,QAASC,WAAW,CAAGf,SAAuB,EAAE,EAEvD,MAAMgB,YAAcZ,YAAY,AAACa,UAC/BF,WAAW,AAACG,MAASA,KAAKC,MAAM,CAAC,AAACC,MAASA,KAAKC,EAAE,GAAKJ,SACzD,EAAG,EAAE,EAEL,MAAMK,WAAalB,YACjB,AAACmB,aACCR,WAAW,AAACG,OACV,MAAMM,QAAUD,WACbJ,MAAM,CACL,AAACM,OACC,CAACP,KAAKQ,IAAI,CACR,AAACC,UACCA,SAASC,OAAO,GAAKH,MAAMG,OAAO,EAClCD,SAASE,IAAI,GAAKJ,MAAMI,IAAI,GAGnCC,GAAG,CAAC,AAACL,OAAW,CAAA,CACf,GAAGA,KAAK,CACRJ,GAAIU,KAAKC,MAAM,GAAGC,QAAQ,CAAC,IAAIC,KAAK,CAAC,GACrCC,QAAS,MACTnB,WACF,CAAA,GAEF,MAAO,IAAIE,QAASM,QAAQ,AAC9B,EACF,EACA,CAACR,YAAY,EAGf,MAAMoB,aAAe/B,QACnB,IAAO,CAAA,CAAES,QAASQ,WAAYN,WAAY,CAAA,EAC1C,CAACF,QAASQ,WAAYN,YAAY,EAGpC,OACE,oBAACN,aAAa2B,QAAQ,EAACC,MAAOF,cAC3BvB,SAGP,EAEA,MAAM0B,gBAAkB,KACtB,MAAMC,QAAUrC,WAAWO,cAC3B,GAAI8B,UAAY7B,UAAW,CACzB,MAAM,IAAI8B,MAAM,oDAClB,CACA,OAAOD,OACT,EAEA,MAAME,eAAiB,CACrBC,MAAO,eACPC,QAAS,eACTC,OAAQ,cACRC,KAAM,cACNC,MAAO,eACT,EAEA,MAAMC,iBAAmB,CACvBL,MAAO,mBACPC,QAAS,oBACTC,OAAQ,oBACRC,KAAM,oBACNC,MAAO,kBACT,EAEA,MAAME,UAAY,CAAC,UAAW,OAAQ,SAAS,CAC/C,MAAMC,eAAiB,IAEvB,MAAMC,YAAc,CAACtB,KAAcuB,cACjC,MAAMC,UAAYpD,OAA6C,MAE/DF,UAAU,KACR,GAAIkD,UAAUK,QAAQ,CAACzB,MAAO,CAC5BwB,UAAUE,OAAO,CAAGC,WAAW,KAC7BJ,YACF,EAAGF,eACL,CAEA,MAAO,KACL,GAAIG,UAAUE,OAAO,CAAE,CACrBE,aAAaJ,UAAUE,OAAO,CAChC,CACF,CACF,EAAG,CAAC1B,KAAMuB,WAAW,CACvB,EAEA,MAAMM,MAAQ,CAAC,CAAErC,EAAE,CAAEQ,IAAI,CAAED,OAAO,CAAEZ,WAAW,CAAc,IAC3D,MAAM2C,IAAM1D,OAAuB,MACnC,KAAM,CAAC2D,OAAQC,UAAU,CAAG7D,SAAS,OACrC,KAAM,CAAC8D,YAAaC,eAAe,CAAG/D,SAAS,GAE/C,MAAMoD,WAAa,KACjB,GAAIO,IAAIJ,OAAO,CAAE,CACfQ,eAAeJ,IAAIJ,OAAO,CAACS,qBAAqB,GAAGC,MAAM,CAC3D,CAEAJ,UAAU,MAEVL,WAAW,KACT,GAAInC,GAAI,CACNL,YAAYK,GACd,CACF,EAAG,IACL,EAEA8B,YAAYtB,KAAMuB,YAElB,MAAMc,aAAe,CAACN,OAEtB,IAAIO,MAEJ,GAAIL,aAAe,CAACF,OAAQ,CAC1BO,MAAQ,CAAEF,OAAQ,CAAC,EAAEH,YAAY,EAAE,CAAC,AAAC,CACvC,MAAO,GAAIF,OAAQ,CACjBO,MAAQ,CAAEF,OAAQ,EAAGG,UAAW,EAAGC,OAAQ,CAAC,CAAE,CAChD,KAAO,CACLF,MAAQ,CAAC,CACX,CAEA,MAAMG,YAAchE,UAAUiE,QAAQ,CAAC3C,QAAS,CAC9C4C,aAAc,CAAC,IAAI,CACnBC,aAAc,CAAC,OAAQ,cAAc,CACrCC,mBAAoB,SACtB,GAEA,MAAMC,UAAmD,CACvD9B,OAAQ,sBACRD,QAAS,yBACTD,MAAO,wCACPI,MAAO,wCACPD,KAAM,EACR,EAEA,MAAM8B,UAAqD,CACzD/B,OAAQG,iBAAiBH,MAAM,CAC/BD,QAASI,iBAAiBJ,OAAO,CACjCD,MAAOK,iBAAiBL,KAAK,CAC7BI,MAAOC,iBAAiBD,KAAK,CAC7BD,KAAM,EACR,EAEA,OACE,oBAAC+B,OACCC,UAAWtE,GACT,8BACA0D,cAAgB,0BAElBC,MAAOA,MACPR,IAAKA,IACLoB,UAAQ,WACRC,cAAY,YAEZ,oBAACH,OACCC,UAAWtE,GACTkC,cAAc,CAACb,KAAK,CACpB,0DAGD8C,SAAS,CAAC9C,KAAK,EAAI+C,SAAS,CAAC/C,KAAK,EACjC,oBAACtB,MACC0E,KAAMN,SAAS,CAAC9C,KAAK,CACrBqD,MAAON,SAAS,CAAC/C,KAAK,CACtBsD,KAAK,SACLC,cAAc,uBAGlB,oBAACC,KACCP,UAAWtE,GAAG,kBAAmBwC,gBAAgB,CAACnB,KAAK,EACvDyD,wBAAyB,CAAEC,OAAQjB,WAAY,IAEjD,oBAACkB,UACC3D,KAAK,SACLiD,UAAU,4CACVW,QAASrC,YAERwB,SAAS,CAAC/C,KAAK,EACd,oBAACtB,MACC0E,KAAK,0BACLC,MAAON,SAAS,CAAC/C,KAAK,CACtBsD,KAAK,SACLC,cAAc,wBAO5B,EAEA,MAAMM,QAAU,KACd,KAAM,CAAE5E,OAAO,CAAEE,WAAW,CAAE,CAAGuB,kBAEjC,OACE,oBAACsC,OAAIC,UAAU,WAAWC,UAAStE,eAChCK,QACEK,MAAM,CAAC,AAACC,MAAS,CAACA,KAAKe,OAAO,EAC9BL,GAAG,CAAC,AAACL,OACJ,oBAACiC,OAAMiC,IAAKlE,MAAMJ,EAAE,CAAG,GAAGI,KAAK,CAAET,YAAaA,eAIxD,EAEA,MAAM4E,eAAiB,CAAC,CAAE9E,OAAO,CAAuB,IACtD,MAAM0B,QAAUrC,WAAWO,cAE3BX,UAAU,KACR,GAAI,CAACyC,QAAS,CACZqD,QAAQC,IAAI,CAAC,oDACb,MACF,CAEA,MAAMC,mBAAqBjF,QAAQgB,GAAG,CAAC,AAACL,QACtC,KAAM,CAACI,KAAMD,QAAQ,CAAGH,MACxB,MAAO,CAAEI,KAAMA,KAAwBD,OAAQ,CACjD,GAEA,GAAImE,mBAAmBC,MAAM,CAAG,EAAG,CACjCxD,QAAQlB,UAAU,CAACyE,mBACrB,CAGF,EAAG,CAACjF,QAAQ,EAEZ,GAAI,CAAC0B,QAAS,OAAO,KAErB,OAAO,oBAACkD,aACV,CAEA,QAASjF,aAAa,CAAEiF,OAAO,CAAE9E,aAAa,CAAE2B,eAAe,CAAG,AAClE,gBAAeqD,cAAe"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as React from"react";import{forwardRef}from"react";const IconDisplayEphemeralMessagesDarkCol=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:44,height:44,fill:"none",viewBox:"0 0 44 44",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"#FF5416",strokeDasharray:"1.5 5",strokeLinecap:"round",strokeWidth:1.5,d:"M.75 21.75c0 11.598 9.402 21 21 21s21-9.402 21-21-9.402-21-21-21"}),React.createElement("rect",{width:22,height:16,x:10.75,y:13.75,stroke:"#C6CED9",strokeWidth:1.607,rx:1.863}),React.createElement("path",{stroke:"#C6CED9",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.5,d:"m11.75 14.75 10.31 7 9.69-7"}),React.createElement("path",{stroke:"#FF5416",strokeDasharray:"1.5 5",strokeLinecap:"round",strokeWidth:1.5,d:"M.75 21.75c0-11.598 9.402-21 21-21"}));const ForwardRef=forwardRef(IconDisplayEphemeralMessagesDarkCol);export default ForwardRef;
|
|
2
|
+
//# sourceMappingURL=icon-display-ephemeral-messages-dark-col.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/Icon/components/icon-display-ephemeral-messages-dark-col.tsx"],"sourcesContent":["import * as React from \"react\";\nimport type { SVGProps } from \"react\";\nimport { Ref, forwardRef } from \"react\";\ninterface SVGRProps {\n title?: string;\n titleId?: string;\n}\nconst IconDisplayEphemeralMessagesDarkCol = ({\n title,\n titleId,\n ...props\n}: SVGProps<SVGSVGElement> & SVGRProps, ref: Ref<SVGSVGElement>) => <svg xmlns=\"http://www.w3.org/2000/svg\" width={44} height={44} fill=\"none\" viewBox=\"0 0 44 44\" ref={ref} aria-labelledby={titleId} {...props}>{title ? <title id={titleId}>{title}</title> : null}<path stroke=\"#FF5416\" strokeDasharray=\"1.5 5\" strokeLinecap=\"round\" strokeWidth={1.5} d=\"M.75 21.75c0 11.598 9.402 21 21 21s21-9.402 21-21-9.402-21-21-21\" /><rect width={22} height={16} x={10.75} y={13.75} stroke=\"#C6CED9\" strokeWidth={1.607} rx={1.863} /><path stroke=\"#C6CED9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5} d=\"m11.75 14.75 10.31 7 9.69-7\" /><path stroke=\"#FF5416\" strokeDasharray=\"1.5 5\" strokeLinecap=\"round\" strokeWidth={1.5} d=\"M.75 21.75c0-11.598 9.402-21 21-21\" /></svg>;\nconst ForwardRef = forwardRef(IconDisplayEphemeralMessagesDarkCol);\nexport default ForwardRef;"],"names":["React","forwardRef","IconDisplayEphemeralMessagesDarkCol","title","titleId","props","ref","svg","xmlns","width","height","fill","viewBox","aria-labelledby","id","path","stroke","strokeDasharray","strokeLinecap","strokeWidth","d","rect","x","y","rx","strokeLinejoin","ForwardRef"],"mappings":"AAAA,UAAYA,UAAW,OAAQ,AAE/B,QAAcC,UAAU,KAAQ,OAAQ,CAKxC,MAAMC,oCAAsC,CAAC,CAC3CC,KAAK,CACLC,OAAO,CACP,GAAGC,MACiC,CAAEC,MAA4B,oBAACC,OAAIC,MAAM,6BAA6BC,MAAO,GAAIC,OAAQ,GAAIC,KAAK,OAAOC,QAAQ,YAAYN,IAAKA,IAAKO,kBAAiBT,QAAU,GAAGC,KAAK,EAAGF,MAAQ,oBAACA,SAAMW,GAAIV,SAAUD,OAAiB,KAAK,oBAACY,QAAKC,OAAO,UAAUC,gBAAgB,QAAQC,cAAc,QAAQC,YAAa,IAAKC,EAAE,qEAAqE,oBAACC,QAAKZ,MAAO,GAAIC,OAAQ,GAAIY,EAAG,MAAOC,EAAG,MAAOP,OAAO,UAAUG,YAAa,MAAOK,GAAI,QAAS,oBAACT,QAAKC,OAAO,UAAUE,cAAc,QAAQO,eAAe,QAAQN,YAAa,IAAKC,EAAE,gCAAgC,oBAACL,QAAKC,OAAO,UAAUC,gBAAgB,QAAQC,cAAc,QAAQC,YAAa,IAAKC,EAAE,wCACxtB,MAAMM,WAAazB,WAAWC,oCAC9B,gBAAewB,UAAW"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as React from"react";import{forwardRef}from"react";const IconDisplayMessageAnnotationsDarkCol=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:45,height:48,fill:"none",viewBox:"0 0 45 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#C6CED9",fillRule:"evenodd",d:"M16.137 42.326a.75.75 0 0 1-1.024.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .274 1.024m12.726 0a.75.75 0 0 1 .275-1.024l.01-.006a.75.75 0 0 1 .75 1.299l-.01.006a.75.75 0 0 1-1.025-.275m5.018-2.897a.75.75 0 0 1 .274-1.024l.01-.006a.75.75 0 1 1 .75 1.3l-.01.005a.75.75 0 0 1-1.024-.275m-22.761 0a.75.75 0 0 1-1.025.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .275 1.024m30.433-11.776a.75.75 0 0 1-.75-.75v-.012a.75.75 0 1 1 1.5 0v.012a.75.75 0 0 1-.75.75m-38.105 0a.75.75 0 0 1-.75-.75v-.012a.75.75 0 0 1 1.5 0v.012a.75.75 0 0 1-.75.75m38.105-5.794a.75.75 0 0 1-.75-.75v-.012a.75.75 0 0 1 1.5 0v.012a.75.75 0 0 1-.75.75m-38.105 0a.75.75 0 0 1-.75-.75v-.012a.75.75 0 0 1 1.5 0v.012a.75.75 0 0 1-.75.75M35.19 9.326a.75.75 0 0 1-1.025.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .275 1.024m-25.38 0a.75.75 0 0 1 .275-1.024l.01-.006a.75.75 0 0 1 .75 1.299l-.01.006a.75.75 0 0 1-1.024-.275M30.173 6.43a.75.75 0 0 1-1.024.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .274 1.024m-15.344 0a.75.75 0 0 1 .275-1.024l.01-.006a.75.75 0 1 1 .75 1.3l-.01.005a.75.75 0 0 1-1.025-.275",clipRule:"evenodd"}),React.createElement("path",{fill:"#FF5416",fillRule:"evenodd",d:"M22.5 8a4 4 0 1 0 0-8 4 4 0 0 0 0 8",clipRule:"evenodd"}),React.createElement("path",{fill:"#FF5416",d:"M40.5 38a4 4 0 1 0 0-8 4 4 0 0 0 0 8"}),React.createElement("path",{fill:"#FF5416",fillRule:"evenodd",d:"M1.91 10a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1zM19.5 40a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1zM41.366 10.5a1 1 0 0 0-1.732 0l-3.464 6a1 1 0 0 0 .866 1.5h6.928a1 1 0 0 0 .866-1.5zM5.332 30.5a1 1 0 0 0-1.732 0l-3.465 6a1 1 0 0 0 .866 1.5H7.93a1 1 0 0 0 .866-1.5z",clipRule:"evenodd"}),React.createElement("rect",{width:20.125,height:14,x:12.688,y:17,stroke:"#C6CED9",strokeWidth:1.5,rx:1.75}),React.createElement("path",{stroke:"#C6CED9",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.5,d:"m13.344 17.656 9.698 6.782 9.114-6.782"}));const ForwardRef=forwardRef(IconDisplayMessageAnnotationsDarkCol);export default ForwardRef;
|
|
2
|
+
//# sourceMappingURL=icon-display-message-annotations-dark-col.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/Icon/components/icon-display-message-annotations-dark-col.tsx"],"sourcesContent":["import * as React from \"react\";\nimport type { SVGProps } from \"react\";\nimport { Ref, forwardRef } from \"react\";\ninterface SVGRProps {\n title?: string;\n titleId?: string;\n}\nconst IconDisplayMessageAnnotationsDarkCol = ({\n title,\n titleId,\n ...props\n}: SVGProps<SVGSVGElement> & SVGRProps, ref: Ref<SVGSVGElement>) => <svg xmlns=\"http://www.w3.org/2000/svg\" width={45} height={48} fill=\"none\" viewBox=\"0 0 45 48\" ref={ref} aria-labelledby={titleId} {...props}>{title ? <title id={titleId}>{title}</title> : null}<path fill=\"#C6CED9\" fillRule=\"evenodd\" d=\"M16.137 42.326a.75.75 0 0 1-1.024.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .274 1.024m12.726 0a.75.75 0 0 1 .275-1.024l.01-.006a.75.75 0 0 1 .75 1.299l-.01.006a.75.75 0 0 1-1.025-.275m5.018-2.897a.75.75 0 0 1 .274-1.024l.01-.006a.75.75 0 1 1 .75 1.3l-.01.005a.75.75 0 0 1-1.024-.275m-22.761 0a.75.75 0 0 1-1.025.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .275 1.024m30.433-11.776a.75.75 0 0 1-.75-.75v-.012a.75.75 0 1 1 1.5 0v.012a.75.75 0 0 1-.75.75m-38.105 0a.75.75 0 0 1-.75-.75v-.012a.75.75 0 0 1 1.5 0v.012a.75.75 0 0 1-.75.75m38.105-5.794a.75.75 0 0 1-.75-.75v-.012a.75.75 0 0 1 1.5 0v.012a.75.75 0 0 1-.75.75m-38.105 0a.75.75 0 0 1-.75-.75v-.012a.75.75 0 0 1 1.5 0v.012a.75.75 0 0 1-.75.75M35.19 9.326a.75.75 0 0 1-1.025.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .275 1.024m-25.38 0a.75.75 0 0 1 .275-1.024l.01-.006a.75.75 0 0 1 .75 1.299l-.01.006a.75.75 0 0 1-1.024-.275M30.173 6.43a.75.75 0 0 1-1.024.275l-.01-.006a.75.75 0 0 1 .75-1.299l.01.006a.75.75 0 0 1 .274 1.024m-15.344 0a.75.75 0 0 1 .275-1.024l.01-.006a.75.75 0 1 1 .75 1.3l-.01.005a.75.75 0 0 1-1.025-.275\" clipRule=\"evenodd\" /><path fill=\"#FF5416\" fillRule=\"evenodd\" d=\"M22.5 8a4 4 0 1 0 0-8 4 4 0 0 0 0 8\" clipRule=\"evenodd\" /><path fill=\"#FF5416\" d=\"M40.5 38a4 4 0 1 0 0-8 4 4 0 0 0 0 8\" /><path fill=\"#FF5416\" fillRule=\"evenodd\" d=\"M1.91 10a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1zM19.5 40a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1zM41.366 10.5a1 1 0 0 0-1.732 0l-3.464 6a1 1 0 0 0 .866 1.5h6.928a1 1 0 0 0 .866-1.5zM5.332 30.5a1 1 0 0 0-1.732 0l-3.465 6a1 1 0 0 0 .866 1.5H7.93a1 1 0 0 0 .866-1.5z\" clipRule=\"evenodd\" /><rect width={20.125} height={14} x={12.688} y={17} stroke=\"#C6CED9\" strokeWidth={1.5} rx={1.75} /><path stroke=\"#C6CED9\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5} d=\"m13.344 17.656 9.698 6.782 9.114-6.782\" /></svg>;\nconst ForwardRef = forwardRef(IconDisplayMessageAnnotationsDarkCol);\nexport default ForwardRef;"],"names":["React","forwardRef","IconDisplayMessageAnnotationsDarkCol","title","titleId","props","ref","svg","xmlns","width","height","fill","viewBox","aria-labelledby","id","path","fillRule","d","clipRule","rect","x","y","stroke","strokeWidth","rx","strokeLinecap","strokeLinejoin","ForwardRef"],"mappings":"AAAA,UAAYA,UAAW,OAAQ,AAE/B,QAAcC,UAAU,KAAQ,OAAQ,CAKxC,MAAMC,qCAAuC,CAAC,CAC5CC,KAAK,CACLC,OAAO,CACP,GAAGC,MACiC,CAAEC,MAA4B,oBAACC,OAAIC,MAAM,6BAA6BC,MAAO,GAAIC,OAAQ,GAAIC,KAAK,OAAOC,QAAQ,YAAYN,IAAKA,IAAKO,kBAAiBT,QAAU,GAAGC,KAAK,EAAGF,MAAQ,oBAACA,SAAMW,GAAIV,SAAUD,OAAiB,KAAK,oBAACY,QAAKJ,KAAK,UAAUK,SAAS,UAAUC,EAAE,smCAAsmCC,SAAS,YAAY,oBAACH,QAAKJ,KAAK,UAAUK,SAAS,UAAUC,EAAE,sCAAsCC,SAAS,YAAY,oBAACH,QAAKJ,KAAK,UAAUM,EAAE,yCAAyC,oBAACF,QAAKJ,KAAK,UAAUK,SAAS,UAAUC,EAAE,yTAAyTC,SAAS,YAAY,oBAACC,QAAKV,MAAO,OAAQC,OAAQ,GAAIU,EAAG,OAAQC,EAAG,GAAIC,OAAO,UAAUC,YAAa,IAAKC,GAAI,OAAQ,oBAACT,QAAKO,OAAO,UAAUG,cAAc,QAAQC,eAAe,QAAQH,YAAa,IAAKN,EAAE,4CACloE,MAAMU,WAAa1B,WAAWC,qCAC9B,gBAAeyB,UAAW"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as React from"react";import{forwardRef}from"react";const IconGuiSquare3Stack3d=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"currentColor",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"#03020D",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.5,d:"M6.429 9.75 2.25 12l4.179 2.25m0-4.5 5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0 4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0-5.571 3-5.571-3"}));const ForwardRef=forwardRef(IconGuiSquare3Stack3d);export default ForwardRef;
|
|
2
|
+
//# sourceMappingURL=icon-gui-square-3-stack-3d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/Icon/components/icon-gui-square-3-stack-3d.tsx"],"sourcesContent":["import * as React from \"react\";\nimport type { SVGProps } from \"react\";\nimport { Ref, forwardRef } from \"react\";\ninterface SVGRProps {\n title?: string;\n titleId?: string;\n}\nconst IconGuiSquare3Stack3d = ({\n title,\n titleId,\n ...props\n}: SVGProps<SVGSVGElement> & SVGRProps, ref: Ref<SVGSVGElement>) => <svg xmlns=\"http://www.w3.org/2000/svg\" width={24} height={24} fill=\"currentColor\" viewBox=\"0 0 24 24\" ref={ref} aria-labelledby={titleId} {...props}>{title ? <title id={titleId}>{title}</title> : null}<path stroke=\"#03020D\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5} d=\"M6.429 9.75 2.25 12l4.179 2.25m0-4.5 5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0 4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0-5.571 3-5.571-3\" /></svg>;\nconst ForwardRef = forwardRef(IconGuiSquare3Stack3d);\nexport default ForwardRef;"],"names":["React","forwardRef","IconGuiSquare3Stack3d","title","titleId","props","ref","svg","xmlns","width","height","fill","viewBox","aria-labelledby","id","path","stroke","strokeLinecap","strokeLinejoin","strokeWidth","d","ForwardRef"],"mappings":"AAAA,UAAYA,UAAW,OAAQ,AAE/B,QAAcC,UAAU,KAAQ,OAAQ,CAKxC,MAAMC,sBAAwB,CAAC,CAC7BC,KAAK,CACLC,OAAO,CACP,GAAGC,MACiC,CAAEC,MAA4B,oBAACC,OAAIC,MAAM,6BAA6BC,MAAO,GAAIC,OAAQ,GAAIC,KAAK,eAAeC,QAAQ,YAAYN,IAAKA,IAAKO,kBAAiBT,QAAU,GAAGC,KAAK,EAAGF,MAAQ,oBAACA,SAAMW,GAAIV,SAAUD,OAAiB,KAAK,oBAACY,QAAKC,OAAO,UAAUC,cAAc,QAAQC,eAAe,QAAQC,YAAa,IAAKC,EAAE,yMACtW,MAAMC,WAAapB,WAAWC,sBAC9B,gBAAemB,UAAW"}
|