@proulr/sdui-components 0.1.0 → 0.1.3

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.
@@ -0,0 +1,1246 @@
1
+ import { Platform, StyleSheet, Text, View } from 'react-native';
2
+ import { m3Colors } from '../theme/proulr-theme';
3
+
4
+ export type AppChromeGlyphProps = {
5
+ color?: string;
6
+ size?: number;
7
+ };
8
+
9
+ function strokeFor(size: number) {
10
+ return Math.max(1.8, size * 0.075);
11
+ }
12
+
13
+ export function AppChromeSearchGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
14
+ const stroke = strokeFor(size);
15
+ const lens = size * 0.48;
16
+ return (
17
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
18
+ <View
19
+ style={{
20
+ width: lens,
21
+ height: lens,
22
+ borderRadius: lens / 2,
23
+ borderWidth: stroke,
24
+ borderColor: color,
25
+ }}
26
+ />
27
+ <View
28
+ style={{
29
+ position: 'absolute',
30
+ width: size * 0.26,
31
+ height: stroke,
32
+ borderRadius: 1,
33
+ backgroundColor: color,
34
+ transform: [{ rotate: '45deg' }],
35
+ bottom: size * 0.14,
36
+ right: size * 0.12,
37
+ }}
38
+ />
39
+ </View>
40
+ );
41
+ }
42
+
43
+ export function AppChromeHuntGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
44
+ const stroke = strokeFor(size);
45
+ const ring = size * 0.82;
46
+ return (
47
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
48
+ <View
49
+ style={{
50
+ width: ring,
51
+ height: ring,
52
+ borderRadius: ring / 2,
53
+ borderWidth: stroke,
54
+ borderColor: color,
55
+ alignItems: 'center',
56
+ justifyContent: 'center',
57
+ }}
58
+ >
59
+ <View
60
+ style={{
61
+ width: ring * 0.3,
62
+ height: ring * 0.3,
63
+ borderRadius: ring * 0.15,
64
+ backgroundColor: color,
65
+ }}
66
+ />
67
+ </View>
68
+ <View style={[styles.crosshair, { width: size * 0.92, height: stroke, backgroundColor: color }]} />
69
+ <View style={[styles.crosshair, { width: stroke, height: size * 0.92, backgroundColor: color }]} />
70
+ </View>
71
+ );
72
+ }
73
+
74
+ export function AppChromeBackGlyph({ color = m3Colors.onSurface, size = 24 }: AppChromeGlyphProps) {
75
+ const stroke = strokeFor(size) + 0.2;
76
+ return (
77
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
78
+ <View
79
+ style={{
80
+ width: size * 0.36,
81
+ height: size * 0.36,
82
+ borderLeftWidth: stroke,
83
+ borderBottomWidth: stroke,
84
+ borderColor: color,
85
+ transform: [{ rotate: '45deg' }],
86
+ marginLeft: size * 0.1,
87
+ }}
88
+ />
89
+ </View>
90
+ );
91
+ }
92
+
93
+ export function AppChromeOverflowGlyph({ color = m3Colors.onSurface, size = 24 }: AppChromeGlyphProps) {
94
+ const dot = size * 0.13;
95
+ return (
96
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center', gap: dot * 0.75 }}>
97
+ <View style={{ width: dot, height: dot, borderRadius: dot / 2, backgroundColor: color }} />
98
+ <View style={{ width: dot, height: dot, borderRadius: dot / 2, backgroundColor: color }} />
99
+ <View style={{ width: dot, height: dot, borderRadius: dot / 2, backgroundColor: color }} />
100
+ </View>
101
+ );
102
+ }
103
+
104
+ export function AppChromeLocationGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
105
+ const stroke = strokeFor(size);
106
+ const head = size * 0.42;
107
+ return (
108
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
109
+ <View
110
+ style={{
111
+ width: head,
112
+ height: head,
113
+ borderRadius: head / 2,
114
+ borderWidth: stroke,
115
+ borderColor: color,
116
+ alignItems: 'center',
117
+ justifyContent: 'center',
118
+ position: 'absolute',
119
+ top: size * 0.06,
120
+ }}
121
+ >
122
+ <View
123
+ style={{
124
+ width: head * 0.34,
125
+ height: head * 0.34,
126
+ borderRadius: head * 0.17,
127
+ backgroundColor: color,
128
+ }}
129
+ />
130
+ </View>
131
+ <View
132
+ style={{
133
+ position: 'absolute',
134
+ bottom: size * 0.04,
135
+ width: 0,
136
+ height: 0,
137
+ borderLeftWidth: head * 0.52,
138
+ borderRightWidth: head * 0.52,
139
+ borderTopWidth: size * 0.3,
140
+ borderLeftColor: 'transparent',
141
+ borderRightColor: 'transparent',
142
+ borderTopColor: color,
143
+ }}
144
+ />
145
+ </View>
146
+ );
147
+ }
148
+
149
+ export function AppChromeMicGlyph({ color = m3Colors.onPrimary, size = 24 }: AppChromeGlyphProps) {
150
+ const stroke = strokeFor(size);
151
+ const capsuleW = size * 0.38;
152
+ const capsuleH = size * 0.56;
153
+ return (
154
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
155
+ <View
156
+ style={{
157
+ width: capsuleW,
158
+ height: capsuleH,
159
+ borderRadius: capsuleW / 2,
160
+ borderWidth: stroke,
161
+ borderColor: color,
162
+ }}
163
+ />
164
+ <View style={{ width: size * 0.48, height: stroke, backgroundColor: color, marginTop: 2 }} />
165
+ <View style={{ width: stroke, height: size * 0.16, backgroundColor: color }} />
166
+ </View>
167
+ );
168
+ }
169
+
170
+ export function AppChromeSendGlyph({ color = m3Colors.onPrimary, size = 24 }: AppChromeGlyphProps) {
171
+ return (
172
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
173
+ <View
174
+ style={{
175
+ width: 0,
176
+ height: 0,
177
+ borderTopWidth: size * 0.24,
178
+ borderBottomWidth: size * 0.24,
179
+ borderLeftWidth: size * 0.38,
180
+ borderTopColor: 'transparent',
181
+ borderBottomColor: 'transparent',
182
+ borderLeftColor: color,
183
+ marginLeft: size * 0.1,
184
+ }}
185
+ />
186
+ </View>
187
+ );
188
+ }
189
+
190
+ export function AppChromeChatGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
191
+ const stroke = strokeFor(size);
192
+ const bubbleW = size * 0.86;
193
+ const bubbleH = size * 0.62;
194
+ return (
195
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
196
+ <View
197
+ style={{
198
+ width: bubbleW,
199
+ height: bubbleH,
200
+ borderRadius: size * 0.16,
201
+ borderWidth: stroke,
202
+ borderColor: color,
203
+ }}
204
+ />
205
+ <View
206
+ style={{
207
+ position: 'absolute',
208
+ bottom: size * 0.1,
209
+ left: size * 0.18,
210
+ width: size * 0.16,
211
+ height: size * 0.16,
212
+ borderBottomWidth: stroke,
213
+ borderLeftWidth: stroke,
214
+ borderColor: color,
215
+ transform: [{ rotate: '-45deg' }],
216
+ }}
217
+ />
218
+ </View>
219
+ );
220
+ }
221
+
222
+ export function AppChromeCloseGlyph({ color = m3Colors.onSurface, size = 20 }: AppChromeGlyphProps) {
223
+ const stroke = strokeFor(size);
224
+ const bar = size * 0.68;
225
+ return (
226
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
227
+ <View style={[styles.diag, { width: bar, height: stroke, backgroundColor: color, transform: [{ rotate: '45deg' }] }]} />
228
+ <View style={[styles.diag, { width: bar, height: stroke, backgroundColor: color, transform: [{ rotate: '-45deg' }] }]} />
229
+ </View>
230
+ );
231
+ }
232
+
233
+ export function AppChromeChevronDownGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
234
+ const stroke = strokeFor(size);
235
+ return (
236
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
237
+ <View
238
+ style={{
239
+ width: size * 0.36,
240
+ height: size * 0.36,
241
+ borderRightWidth: stroke,
242
+ borderBottomWidth: stroke,
243
+ borderColor: color,
244
+ transform: [{ rotate: '45deg' }],
245
+ marginTop: -size * 0.06,
246
+ }}
247
+ />
248
+ </View>
249
+ );
250
+ }
251
+
252
+ export function AppChromeChevronRightGlyph({ color = m3Colors.onSurfaceVariant, size = 20 }: AppChromeGlyphProps) {
253
+ const stroke = strokeFor(size);
254
+ return (
255
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
256
+ <View
257
+ style={{
258
+ width: size * 0.36,
259
+ height: size * 0.36,
260
+ borderRightWidth: stroke,
261
+ borderBottomWidth: stroke,
262
+ borderColor: color,
263
+ transform: [{ rotate: '-45deg' }],
264
+ marginLeft: -size * 0.06,
265
+ }}
266
+ />
267
+ </View>
268
+ );
269
+ }
270
+
271
+ export function AppChromeFlipCameraGlyph({ color = m3Colors.onPrimary, size = 24 }: AppChromeGlyphProps) {
272
+ const stroke = strokeFor(size);
273
+ const ring = size * 0.78;
274
+ return (
275
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
276
+ <View
277
+ style={{
278
+ width: ring,
279
+ height: ring,
280
+ borderRadius: ring / 2,
281
+ borderWidth: stroke,
282
+ borderColor: color,
283
+ borderTopColor: 'transparent',
284
+ transform: [{ rotate: '45deg' }],
285
+ }}
286
+ />
287
+ <View
288
+ style={{
289
+ position: 'absolute',
290
+ top: size * 0.1,
291
+ right: size * 0.12,
292
+ width: 0,
293
+ height: 0,
294
+ borderTopWidth: size * 0.11,
295
+ borderBottomWidth: size * 0.11,
296
+ borderLeftWidth: size * 0.13,
297
+ borderTopColor: 'transparent',
298
+ borderBottomColor: 'transparent',
299
+ borderLeftColor: color,
300
+ transform: [{ rotate: '-30deg' }],
301
+ }}
302
+ />
303
+ </View>
304
+ );
305
+ }
306
+
307
+ /** Flash — estalo / sinal de interesse (centro + 4 raios longos + 4 curtos). */
308
+ export function AppChromeFlashGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
309
+ const core = size * 0.22;
310
+ const mainRayW = Math.max(2.6, size * 0.12);
311
+ const mainRayLen = size * 0.42;
312
+ const diagRayW = Math.max(2, size * 0.09);
313
+ const diagRayLen = size * 0.28;
314
+ const mainAngles = [0, 90, 180, 270];
315
+ const diagAngles = [45, 135, 225, 315];
316
+
317
+ const rayStyle = (rayW: number, rayLen: number, angle: number) => ({
318
+ position: 'absolute' as const,
319
+ width: rayW,
320
+ height: rayLen,
321
+ borderRadius: rayW / 2,
322
+ backgroundColor: color,
323
+ left: size / 2 - rayW / 2,
324
+ top: size / 2 - rayLen / 2,
325
+ transform: [{ rotate: `${angle}deg` }, { translateY: -size * 0.28 }],
326
+ });
327
+
328
+ return (
329
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
330
+ {mainAngles.map((angle) => (
331
+ <View key={`m-${angle}`} style={rayStyle(mainRayW, mainRayLen, angle)} />
332
+ ))}
333
+ {diagAngles.map((angle) => (
334
+ <View key={`d-${angle}`} style={rayStyle(diagRayW, diagRayLen, angle)} />
335
+ ))}
336
+ <View
337
+ style={{
338
+ width: core,
339
+ height: core,
340
+ borderRadius: core / 2,
341
+ backgroundColor: color,
342
+ }}
343
+ />
344
+ </View>
345
+ );
346
+ }
347
+
348
+ export function AppChromeTabDiscoverGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
349
+ return <AppChromeHuntGlyph color={color} size={size} />;
350
+ }
351
+
352
+ /** Vibes — play em círculo (feed de vídeo / mídia). */
353
+ export function AppChromeTabVibesGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
354
+ const stroke = strokeFor(size);
355
+ const ring = size * 0.84;
356
+ return (
357
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
358
+ <View
359
+ style={{
360
+ width: ring,
361
+ height: ring,
362
+ borderRadius: ring / 2,
363
+ borderWidth: stroke,
364
+ borderColor: color,
365
+ alignItems: 'center',
366
+ justifyContent: 'center',
367
+ }}
368
+ >
369
+ <View
370
+ style={{
371
+ width: 0,
372
+ height: 0,
373
+ borderTopWidth: size * 0.14,
374
+ borderBottomWidth: size * 0.14,
375
+ borderLeftWidth: size * 0.22,
376
+ borderTopColor: 'transparent',
377
+ borderBottomColor: 'transparent',
378
+ borderLeftColor: color,
379
+ marginLeft: size * 0.05,
380
+ }}
381
+ />
382
+ </View>
383
+ </View>
384
+ );
385
+ }
386
+
387
+ export function AppChromeTabFlashGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
388
+ return <AppChromeFlashGlyph color={color} size={size} />;
389
+ }
390
+
391
+ export function AppChromeTabChatGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
392
+ return <AppChromeChatGlyph color={color} size={size} />;
393
+ }
394
+
395
+ /** Configurações — sliders horizontais (não confundir com alvo de caçar). */
396
+ export function AppChromeSettingsGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
397
+ const stroke = strokeFor(size);
398
+ const trackW = size * 0.72;
399
+ const knob = size * 0.14;
400
+ const rows = [
401
+ { top: size * 0.22, knobLeft: size * 0.14 },
402
+ { top: size * 0.44, knobLeft: size * 0.42 },
403
+ { top: size * 0.66, knobLeft: size * 0.24 },
404
+ ];
405
+ return (
406
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
407
+ {rows.map((row) => (
408
+ <View
409
+ key={row.top}
410
+ style={{
411
+ position: 'absolute',
412
+ top: row.top,
413
+ width: trackW,
414
+ height: stroke,
415
+ backgroundColor: color,
416
+ borderRadius: 1,
417
+ opacity: 0.45,
418
+ }}
419
+ />
420
+ ))}
421
+ {rows.map((row) => (
422
+ <View
423
+ key={`knob-${row.top}`}
424
+ style={{
425
+ position: 'absolute',
426
+ top: row.top - knob / 2 + stroke / 2,
427
+ left: row.knobLeft,
428
+ width: knob,
429
+ height: knob,
430
+ borderRadius: knob / 2,
431
+ borderWidth: stroke,
432
+ borderColor: color,
433
+ backgroundColor: color,
434
+ }}
435
+ />
436
+ ))}
437
+ </View>
438
+ );
439
+ }
440
+
441
+ export function AppChromeBoostGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
442
+ const stroke = strokeFor(size);
443
+ const bodyW = size * 0.34;
444
+ const bodyH = size * 0.46;
445
+ return (
446
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
447
+ <View
448
+ style={{
449
+ position: 'absolute',
450
+ top: size * 0.1,
451
+ width: 0,
452
+ height: 0,
453
+ borderLeftWidth: size * 0.12,
454
+ borderRightWidth: size * 0.12,
455
+ borderBottomWidth: size * 0.18,
456
+ borderLeftColor: 'transparent',
457
+ borderRightColor: 'transparent',
458
+ borderBottomColor: color,
459
+ }}
460
+ />
461
+ <View
462
+ style={{
463
+ position: 'absolute',
464
+ top: size * 0.26,
465
+ width: bodyW,
466
+ height: bodyH,
467
+ borderRadius: size * 0.06,
468
+ borderWidth: stroke,
469
+ borderColor: color,
470
+ alignItems: 'center',
471
+ justifyContent: 'center',
472
+ }}
473
+ >
474
+ <View
475
+ style={{
476
+ width: bodyW * 0.38,
477
+ height: bodyW * 0.38,
478
+ borderRadius: bodyW * 0.19,
479
+ borderWidth: stroke * 0.8,
480
+ borderColor: color,
481
+ }}
482
+ />
483
+ </View>
484
+ <View
485
+ style={{
486
+ position: 'absolute',
487
+ bottom: size * 0.14,
488
+ left: size * 0.28,
489
+ width: size * 0.16,
490
+ height: size * 0.12,
491
+ borderBottomWidth: stroke,
492
+ borderLeftWidth: stroke,
493
+ borderColor: color,
494
+ transform: [{ rotate: '-35deg' }],
495
+ }}
496
+ />
497
+ <View
498
+ style={{
499
+ position: 'absolute',
500
+ bottom: size * 0.14,
501
+ right: size * 0.28,
502
+ width: size * 0.16,
503
+ height: size * 0.12,
504
+ borderBottomWidth: stroke,
505
+ borderRightWidth: stroke,
506
+ borderColor: color,
507
+ transform: [{ rotate: '35deg' }],
508
+ }}
509
+ />
510
+ </View>
511
+ );
512
+ }
513
+
514
+ export function AppChromeFireGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
515
+ const stroke = strokeFor(size);
516
+ const w = size * 0.5;
517
+ const h = size * 0.7;
518
+ return (
519
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
520
+ <View
521
+ style={{
522
+ width: w,
523
+ height: h,
524
+ borderTopLeftRadius: w * 0.55,
525
+ borderTopRightRadius: w * 0.55,
526
+ borderBottomLeftRadius: w * 0.22,
527
+ borderBottomRightRadius: w * 0.22,
528
+ borderWidth: stroke,
529
+ borderColor: color,
530
+ }}
531
+ />
532
+ <View
533
+ style={{
534
+ position: 'absolute',
535
+ bottom: size * 0.18,
536
+ width: w * 0.34,
537
+ height: h * 0.46,
538
+ borderTopLeftRadius: w * 0.22,
539
+ borderTopRightRadius: w * 0.22,
540
+ borderBottomLeftRadius: w * 0.12,
541
+ borderBottomRightRadius: w * 0.12,
542
+ backgroundColor: color,
543
+ }}
544
+ />
545
+ </View>
546
+ );
547
+ }
548
+
549
+ /** Editar perfil — só lápis (corpo + ponta). */
550
+ export function AppChromeEditGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
551
+ const bodyW = Math.max(3, size * 0.17);
552
+ const bodyH = size * 0.64;
553
+ return (
554
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
555
+ <View
556
+ style={{
557
+ width: bodyW,
558
+ height: bodyH,
559
+ borderRadius: bodyW / 2,
560
+ backgroundColor: color,
561
+ transform: [{ rotate: '-45deg' }],
562
+ }}
563
+ />
564
+ <View
565
+ style={{
566
+ position: 'absolute',
567
+ bottom: size * 0.17,
568
+ left: size * 0.17,
569
+ width: 0,
570
+ height: 0,
571
+ borderLeftWidth: bodyW * 0.95,
572
+ borderRightWidth: bodyW * 0.95,
573
+ borderTopWidth: size * 0.13,
574
+ borderLeftColor: 'transparent',
575
+ borderRightColor: 'transparent',
576
+ borderTopColor: color,
577
+ transform: [{ rotate: '-45deg' }],
578
+ }}
579
+ />
580
+ </View>
581
+ );
582
+ }
583
+
584
+ /** Story — anel com ponto central (criar story). */
585
+ export function AppChromeStoryGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
586
+ const stroke = strokeFor(size);
587
+ const ring = size * 0.82;
588
+ return (
589
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
590
+ <View
591
+ style={{
592
+ width: ring,
593
+ height: ring,
594
+ borderRadius: ring / 2,
595
+ borderWidth: stroke,
596
+ borderColor: color,
597
+ alignItems: 'center',
598
+ justifyContent: 'center',
599
+ }}
600
+ >
601
+ <View
602
+ style={{
603
+ width: ring * 0.34,
604
+ height: ring * 0.34,
605
+ borderRadius: ring * 0.17,
606
+ backgroundColor: color,
607
+ }}
608
+ />
609
+ </View>
610
+ </View>
611
+ );
612
+ }
613
+
614
+ export function AppChromeStoryMusicGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
615
+ const stroke = strokeFor(size);
616
+ const head = size * 0.18;
617
+ return (
618
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
619
+ <View
620
+ style={{
621
+ position: 'absolute',
622
+ left: size * 0.22,
623
+ bottom: size * 0.18,
624
+ width: head,
625
+ height: head,
626
+ borderRadius: head / 2,
627
+ borderWidth: stroke,
628
+ borderColor: color,
629
+ }}
630
+ />
631
+ <View
632
+ style={{
633
+ position: 'absolute',
634
+ left: size * 0.3,
635
+ bottom: size * 0.34,
636
+ width: stroke,
637
+ height: size * 0.42,
638
+ backgroundColor: color,
639
+ }}
640
+ />
641
+ <View
642
+ style={{
643
+ position: 'absolute',
644
+ top: size * 0.16,
645
+ right: size * 0.24,
646
+ width: size * 0.28,
647
+ height: stroke,
648
+ backgroundColor: color,
649
+ transform: [{ rotate: '32deg' }],
650
+ }}
651
+ />
652
+ </View>
653
+ );
654
+ }
655
+
656
+ export function AppChromeStoryStickerGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
657
+ const stroke = strokeFor(size);
658
+ const frame = size * 0.78;
659
+ return (
660
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
661
+ <View
662
+ style={{
663
+ width: frame,
664
+ height: frame,
665
+ borderRadius: size * 0.14,
666
+ borderWidth: stroke,
667
+ borderColor: color,
668
+ alignItems: 'center',
669
+ justifyContent: 'center',
670
+ }}
671
+ >
672
+ <View
673
+ style={{
674
+ flexDirection: 'row',
675
+ gap: frame * 0.14,
676
+ marginBottom: frame * 0.08,
677
+ }}
678
+ >
679
+ <View style={{ width: frame * 0.1, height: frame * 0.1, borderRadius: frame * 0.05, backgroundColor: color }} />
680
+ <View style={{ width: frame * 0.1, height: frame * 0.1, borderRadius: frame * 0.05, backgroundColor: color }} />
681
+ </View>
682
+ <View
683
+ style={{
684
+ width: frame * 0.34,
685
+ height: frame * 0.16,
686
+ borderBottomLeftRadius: frame * 0.17,
687
+ borderBottomRightRadius: frame * 0.17,
688
+ borderWidth: stroke,
689
+ borderTopWidth: 0,
690
+ borderColor: color,
691
+ }}
692
+ />
693
+ </View>
694
+ </View>
695
+ );
696
+ }
697
+
698
+ export function AppChromeStoryTextGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
699
+ const stroke = strokeFor(size);
700
+ const frame = size * 0.78;
701
+ return (
702
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
703
+ <View
704
+ style={{
705
+ width: frame,
706
+ height: frame * 0.72,
707
+ borderRadius: size * 0.1,
708
+ borderWidth: stroke,
709
+ borderColor: color,
710
+ alignItems: 'center',
711
+ justifyContent: 'center',
712
+ }}
713
+ >
714
+ <View style={{ flexDirection: 'row', alignItems: 'flex-end', gap: 1 }}>
715
+ <View style={{ width: stroke * 1.1, height: size * 0.28, backgroundColor: color, borderRadius: 1 }} />
716
+ <View style={{ width: size * 0.2, height: size * 0.2, borderRadius: size * 0.1, borderWidth: stroke, borderColor: color }} />
717
+ </View>
718
+ </View>
719
+ </View>
720
+ );
721
+ }
722
+
723
+ export function AppChromeStoryEffectsGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
724
+ return <AppChromeFlashGlyph color={color} size={size} />;
725
+ }
726
+
727
+ export function AppChromeStoryMentionGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
728
+ const stroke = strokeFor(size);
729
+ const ring = size * 0.78;
730
+ return (
731
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
732
+ <View
733
+ style={{
734
+ width: ring,
735
+ height: ring,
736
+ borderRadius: ring / 2,
737
+ borderWidth: stroke,
738
+ borderColor: color,
739
+ alignItems: 'center',
740
+ justifyContent: 'center',
741
+ }}
742
+ >
743
+ <View
744
+ style={{
745
+ width: stroke * 1.2,
746
+ height: size * 0.34,
747
+ backgroundColor: color,
748
+ borderRadius: 1,
749
+ }}
750
+ />
751
+ <View
752
+ style={{
753
+ position: 'absolute',
754
+ bottom: size * 0.22,
755
+ width: size * 0.14,
756
+ height: size * 0.14,
757
+ borderRadius: size * 0.07,
758
+ borderWidth: stroke,
759
+ borderColor: color,
760
+ }}
761
+ />
762
+ </View>
763
+ </View>
764
+ );
765
+ }
766
+
767
+ export function AppChromeRepostGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
768
+ const stroke = strokeFor(size);
769
+ const shaft = size * 0.36;
770
+ const head = size * 0.1;
771
+ const rowH = stroke;
772
+
773
+ return (
774
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
775
+ {/* seta superior → */}
776
+ <View
777
+ style={{
778
+ position: 'absolute',
779
+ top: size * 0.28,
780
+ left: size * 0.2,
781
+ width: shaft,
782
+ height: rowH,
783
+ backgroundColor: color,
784
+ borderRadius: 1,
785
+ }}
786
+ />
787
+ <View
788
+ style={{
789
+ position: 'absolute',
790
+ top: size * 0.28 + rowH / 2 - head,
791
+ left: size * 0.2 + shaft - head * 0.15,
792
+ width: 0,
793
+ height: 0,
794
+ borderTopWidth: head,
795
+ borderBottomWidth: head,
796
+ borderLeftWidth: head * 1.15,
797
+ borderTopColor: 'transparent',
798
+ borderBottomColor: 'transparent',
799
+ borderLeftColor: color,
800
+ }}
801
+ />
802
+ {/* seta inferior ← */}
803
+ <View
804
+ style={{
805
+ position: 'absolute',
806
+ top: size * 0.56,
807
+ left: size * 0.28,
808
+ width: shaft,
809
+ height: rowH,
810
+ backgroundColor: color,
811
+ borderRadius: 1,
812
+ }}
813
+ />
814
+ <View
815
+ style={{
816
+ position: 'absolute',
817
+ top: size * 0.56 + rowH / 2 - head,
818
+ left: size * 0.22,
819
+ width: 0,
820
+ height: 0,
821
+ borderTopWidth: head,
822
+ borderBottomWidth: head,
823
+ borderRightWidth: head * 1.15,
824
+ borderTopColor: 'transparent',
825
+ borderBottomColor: 'transparent',
826
+ borderRightColor: color,
827
+ }}
828
+ />
829
+ </View>
830
+ );
831
+ }
832
+
833
+ type HeartGlyphProps = AppChromeGlyphProps & { filled?: boolean };
834
+
835
+ function AppChromeHeartShape({ color = m3Colors.primary, size = 24, filled = false }: HeartGlyphProps) {
836
+ const glyph = filled ? '♥' : '♡';
837
+ return (
838
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
839
+ <Text
840
+ style={{
841
+ color,
842
+ fontSize: size * 0.9,
843
+ lineHeight: size,
844
+ height: size,
845
+ textAlign: 'center',
846
+ ...(Platform.OS === 'android'
847
+ ? { includeFontPadding: false, textAlignVertical: 'center' as const }
848
+ : null),
849
+ }}
850
+ >
851
+ {glyph}
852
+ </Text>
853
+ </View>
854
+ );
855
+ }
856
+
857
+ export function AppChromePlayGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
858
+ return (
859
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
860
+ <View
861
+ style={{
862
+ width: 0,
863
+ height: 0,
864
+ borderTopWidth: size * 0.2,
865
+ borderBottomWidth: size * 0.2,
866
+ borderLeftWidth: size * 0.32,
867
+ borderTopColor: 'transparent',
868
+ borderBottomColor: 'transparent',
869
+ borderLeftColor: color,
870
+ marginLeft: size * 0.07,
871
+ }}
872
+ />
873
+ </View>
874
+ );
875
+ }
876
+
877
+ export function AppChromePauseGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
878
+ const barW = Math.max(2.4, size * 0.14);
879
+ const barH = size * 0.4;
880
+ const gap = size * 0.1;
881
+ return (
882
+ <View
883
+ style={{
884
+ width: size,
885
+ height: size,
886
+ flexDirection: 'row',
887
+ alignItems: 'center',
888
+ justifyContent: 'center',
889
+ gap,
890
+ }}
891
+ >
892
+ <View style={{ width: barW, height: barH, borderRadius: 2, backgroundColor: color }} />
893
+ <View style={{ width: barW, height: barH, borderRadius: 2, backgroundColor: color }} />
894
+ </View>
895
+ );
896
+ }
897
+
898
+ export function AppChromeHeartGlyph(props: AppChromeGlyphProps) {
899
+ return <AppChromeHeartShape {...props} filled={false} />;
900
+ }
901
+
902
+ export function AppChromeHeartFilledGlyph(props: AppChromeGlyphProps) {
903
+ return <AppChromeHeartShape {...props} filled />;
904
+ }
905
+
906
+ type ThumbsUpGlyphProps = AppChromeGlyphProps & { filled?: boolean };
907
+
908
+ function AppChromeThumbsUpShape({ color = m3Colors.primary, size = 24, filled = false }: ThumbsUpGlyphProps) {
909
+ const stroke = strokeFor(size);
910
+ const palmW = size * 0.46;
911
+ const palmH = size * 0.34;
912
+ const thumbW = size * 0.2;
913
+ const thumbH = size * 0.36;
914
+ const fill = filled ? color : 'transparent';
915
+ const borderW = filled ? 0 : stroke;
916
+ const radius = Math.max(2, size * 0.07);
917
+
918
+ return (
919
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
920
+ <View style={{ width: palmW, height: palmH + thumbH * 0.62 }}>
921
+ <View
922
+ style={{
923
+ position: 'absolute',
924
+ top: 0,
925
+ right: palmW * 0.04,
926
+ width: thumbW,
927
+ height: thumbH,
928
+ borderRadius: thumbW / 2,
929
+ backgroundColor: fill,
930
+ borderWidth: borderW,
931
+ borderColor: color,
932
+ }}
933
+ />
934
+ <View
935
+ style={{
936
+ position: 'absolute',
937
+ bottom: 0,
938
+ left: 0,
939
+ width: palmW,
940
+ height: palmH,
941
+ borderRadius: radius,
942
+ backgroundColor: fill,
943
+ borderWidth: borderW,
944
+ borderColor: color,
945
+ }}
946
+ />
947
+ </View>
948
+ </View>
949
+ );
950
+ }
951
+
952
+ export function AppChromeThumbsUpGlyph(props: AppChromeGlyphProps) {
953
+ return <AppChromeThumbsUpShape {...props} filled={false} />;
954
+ }
955
+
956
+ export function AppChromeThumbsUpFilledGlyph(props: AppChromeGlyphProps) {
957
+ return <AppChromeThumbsUpShape {...props} filled />;
958
+ }
959
+
960
+ export function AppChromePlusGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
961
+ const stroke = strokeFor(size);
962
+ const arm = size * 0.46;
963
+ return (
964
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
965
+ <View style={{ width: arm, height: stroke, backgroundColor: color, borderRadius: 1 }} />
966
+ <View style={{ position: 'absolute', width: stroke, height: arm, backgroundColor: color, borderRadius: 1 }} />
967
+ </View>
968
+ );
969
+ }
970
+
971
+ /** Publicar — documento com + central. */
972
+ export function AppChromePublishGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
973
+ const stroke = strokeFor(size);
974
+ const frame = size * 0.74;
975
+ const plusLen = frame * 0.42;
976
+ return (
977
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
978
+ <View
979
+ style={{
980
+ width: frame,
981
+ height: frame,
982
+ borderRadius: size * 0.12,
983
+ borderWidth: stroke,
984
+ borderColor: color,
985
+ alignItems: 'center',
986
+ justifyContent: 'center',
987
+ }}
988
+ >
989
+ <View style={{ width: plusLen, height: stroke, backgroundColor: color, borderRadius: 1 }} />
990
+ <View
991
+ style={{
992
+ position: 'absolute',
993
+ width: stroke,
994
+ height: plusLen,
995
+ backgroundColor: color,
996
+ borderRadius: 1,
997
+ }}
998
+ />
999
+ </View>
1000
+ </View>
1001
+ );
1002
+ }
1003
+
1004
+ export function AppChromeIncognitoGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1005
+ const stroke = strokeFor(size);
1006
+ const frameW = size * 0.86;
1007
+ const frameH = size * 0.34;
1008
+ return (
1009
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1010
+ <View
1011
+ style={{
1012
+ width: frameW,
1013
+ height: frameH,
1014
+ borderRadius: frameH / 2,
1015
+ borderWidth: stroke,
1016
+ borderColor: color,
1017
+ marginTop: size * 0.08,
1018
+ }}
1019
+ />
1020
+ <View
1021
+ style={{
1022
+ position: 'absolute',
1023
+ top: size * 0.34,
1024
+ width: frameW * 0.72,
1025
+ height: frameH * 0.9,
1026
+ borderBottomLeftRadius: frameH * 0.45,
1027
+ borderBottomRightRadius: frameH * 0.45,
1028
+ borderWidth: stroke,
1029
+ borderTopWidth: 0,
1030
+ borderColor: color,
1031
+ }}
1032
+ />
1033
+ </View>
1034
+ );
1035
+ }
1036
+
1037
+ export function AppChromeEventsGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1038
+ const stroke = strokeFor(size);
1039
+ const pole = size * 0.12;
1040
+ return (
1041
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1042
+ <View style={{ position: 'absolute', left: size * 0.22, width: pole, height: size * 0.72, backgroundColor: color }} />
1043
+ <View style={{ position: 'absolute', right: size * 0.22, width: pole, height: size * 0.72, backgroundColor: color }} />
1044
+ <View
1045
+ style={{
1046
+ position: 'absolute',
1047
+ top: size * 0.12,
1048
+ width: size * 0.72,
1049
+ height: size * 0.34,
1050
+ borderTopLeftRadius: size * 0.08,
1051
+ borderTopRightRadius: size * 0.08,
1052
+ borderWidth: stroke,
1053
+ borderColor: color,
1054
+ }}
1055
+ />
1056
+ </View>
1057
+ );
1058
+ }
1059
+
1060
+ export function AppChromeShieldGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1061
+ const stroke = strokeFor(size);
1062
+ const w = size * 0.62;
1063
+ const h = size * 0.76;
1064
+ return (
1065
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1066
+ <View
1067
+ style={{
1068
+ width: w,
1069
+ height: h,
1070
+ borderTopLeftRadius: w * 0.2,
1071
+ borderTopRightRadius: w * 0.2,
1072
+ borderBottomLeftRadius: w * 0.45,
1073
+ borderBottomRightRadius: w * 0.45,
1074
+ borderWidth: stroke,
1075
+ borderColor: color,
1076
+ }}
1077
+ />
1078
+ </View>
1079
+ );
1080
+ }
1081
+
1082
+ export function AppChromeProfileGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1083
+ const stroke = strokeFor(size);
1084
+ const head = size * 0.28;
1085
+ return (
1086
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1087
+ <View
1088
+ style={{
1089
+ width: head,
1090
+ height: head,
1091
+ borderRadius: head / 2,
1092
+ borderWidth: stroke,
1093
+ borderColor: color,
1094
+ marginBottom: size * 0.04,
1095
+ }}
1096
+ />
1097
+ <View
1098
+ style={{
1099
+ width: size * 0.62,
1100
+ height: size * 0.3,
1101
+ borderTopLeftRadius: size * 0.3,
1102
+ borderTopRightRadius: size * 0.3,
1103
+ borderWidth: stroke,
1104
+ borderColor: color,
1105
+ borderBottomWidth: 0,
1106
+ }}
1107
+ />
1108
+ </View>
1109
+ );
1110
+ }
1111
+
1112
+ export function AppChromeBookmarkGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1113
+ const stroke = strokeFor(size);
1114
+ const w = size * 0.52;
1115
+ const h = size * 0.72;
1116
+ return (
1117
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1118
+ <View
1119
+ style={{
1120
+ width: w,
1121
+ height: h,
1122
+ borderWidth: stroke,
1123
+ borderColor: color,
1124
+ borderBottomWidth: 0,
1125
+ }}
1126
+ />
1127
+ <View
1128
+ style={{
1129
+ position: 'absolute',
1130
+ bottom: size * 0.12,
1131
+ width: 0,
1132
+ height: 0,
1133
+ borderLeftWidth: w / 2,
1134
+ borderRightWidth: w / 2,
1135
+ borderTopWidth: size * 0.16,
1136
+ borderLeftColor: 'transparent',
1137
+ borderRightColor: 'transparent',
1138
+ borderTopColor: color,
1139
+ }}
1140
+ />
1141
+ </View>
1142
+ );
1143
+ }
1144
+
1145
+ export function AppChromeListGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1146
+ const stroke = strokeFor(size);
1147
+ const lineW = size * 0.62;
1148
+ return (
1149
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center', gap: size * 0.1 }}>
1150
+ {[0, 1, 2].map((row) => (
1151
+ <View key={row} style={{ flexDirection: 'row', alignItems: 'center', gap: size * 0.08 }}>
1152
+ <View style={{ width: size * 0.14, height: size * 0.14, borderRadius: size * 0.07, borderWidth: stroke, borderColor: color }} />
1153
+ <View style={{ width: lineW, height: stroke, backgroundColor: color }} />
1154
+ </View>
1155
+ ))}
1156
+ </View>
1157
+ );
1158
+ }
1159
+
1160
+ export function AppChromeCommunitiesGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1161
+ const stroke = strokeFor(size);
1162
+ const head = size * 0.22;
1163
+ return (
1164
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1165
+ <View style={{ flexDirection: 'row', gap: size * 0.08, marginBottom: size * 0.04 }}>
1166
+ <View style={{ width: head, height: head, borderRadius: head / 2, borderWidth: stroke, borderColor: color }} />
1167
+ <View style={{ width: head, height: head, borderRadius: head / 2, borderWidth: stroke, borderColor: color }} />
1168
+ </View>
1169
+ <View
1170
+ style={{
1171
+ width: size * 0.72,
1172
+ height: size * 0.26,
1173
+ borderTopLeftRadius: size * 0.16,
1174
+ borderTopRightRadius: size * 0.16,
1175
+ borderWidth: stroke,
1176
+ borderColor: color,
1177
+ borderBottomWidth: 0,
1178
+ }}
1179
+ />
1180
+ </View>
1181
+ );
1182
+ }
1183
+
1184
+ export function AppChromeStarGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1185
+ const stroke = strokeFor(size);
1186
+ const arm = size * 0.34;
1187
+ return (
1188
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1189
+ <View style={{ position: 'absolute', width: arm, height: stroke, backgroundColor: color }} />
1190
+ <View style={{ position: 'absolute', width: stroke, height: arm, backgroundColor: color }} />
1191
+ <View style={{ position: 'absolute', width: arm, height: stroke, backgroundColor: color, transform: [{ rotate: '45deg' }] }} />
1192
+ <View style={{ position: 'absolute', width: arm, height: stroke, backgroundColor: color, transform: [{ rotate: '-45deg' }] }} />
1193
+ <View
1194
+ style={{
1195
+ width: size * 0.2,
1196
+ height: size * 0.2,
1197
+ borderRadius: size * 0.1,
1198
+ backgroundColor: color,
1199
+ }}
1200
+ />
1201
+ </View>
1202
+ );
1203
+ }
1204
+
1205
+ export function AppChromeSignOutGlyph({ color = m3Colors.primary, size = 24 }: AppChromeGlyphProps) {
1206
+ const stroke = strokeFor(size);
1207
+ return (
1208
+ <View style={{ width: size, height: size, alignItems: 'center', justifyContent: 'center' }}>
1209
+ <View
1210
+ style={{
1211
+ position: 'absolute',
1212
+ left: size * 0.12,
1213
+ width: size * 0.34,
1214
+ height: size * 0.34,
1215
+ borderRadius: size * 0.17,
1216
+ borderWidth: stroke,
1217
+ borderColor: color,
1218
+ }}
1219
+ />
1220
+ <View style={{ position: 'absolute', right: size * 0.1, width: size * 0.34, height: stroke, backgroundColor: color }} />
1221
+ <View
1222
+ style={{
1223
+ position: 'absolute',
1224
+ right: size * 0.1,
1225
+ width: size * 0.16,
1226
+ height: size * 0.16,
1227
+ borderRightWidth: stroke,
1228
+ borderTopWidth: stroke,
1229
+ borderColor: color,
1230
+ transform: [{ rotate: '45deg' }],
1231
+ }}
1232
+ />
1233
+ </View>
1234
+ );
1235
+ }
1236
+
1237
+ const styles = StyleSheet.create({
1238
+ crosshair: {
1239
+ position: 'absolute',
1240
+ borderRadius: 1,
1241
+ },
1242
+ diag: {
1243
+ position: 'absolute',
1244
+ borderRadius: 1,
1245
+ },
1246
+ });