@refraction-ui/astro 0.16.0 → 0.17.0

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.
@@ -1,7 +1,7 @@
1
1
  import { cva } from '../shared/index.ts'
2
2
 
3
3
  export const emojiPickerContainerStyles =
4
- 'w-80 rounded-lg border bg-popover text-popover-foreground shadow-md'
4
+ 'w-80 rounded-lg border bg-popover text-popover-foreground shadow-md motion-safe:animate-fade-in-scale origin-top'
5
5
 
6
6
  export const emojiPickerSearchStyles =
7
7
  'w-full border-b px-3 py-2 text-sm bg-transparent outline-none placeholder:text-muted-foreground'
@@ -10,7 +10,7 @@ export const emojiPickerCategoryBarStyles =
10
10
  'flex border-b px-1'
11
11
 
12
12
  export const emojiPickerCategoryTabVariants = cva({
13
- base: 'flex-1 py-1.5 text-center text-lg cursor-pointer hover:bg-accent rounded-md transition-colors',
13
+ base: 'flex-1 py-1.5 text-center text-lg cursor-pointer hover:bg-accent rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
14
14
  variants: {
15
15
  state: {
16
16
  active: 'bg-accent',
@@ -26,7 +26,15 @@ export const emojiPickerGridStyles =
26
26
  'grid grid-cols-8 gap-0.5 p-2 max-h-64 overflow-y-auto'
27
27
 
28
28
  export const emojiPickerEmojiButtonStyles =
29
- 'flex items-center justify-center h-8 w-8 rounded cursor-pointer text-lg hover:bg-accent transition-colors'
29
+ 'flex items-center justify-center h-8 w-8 rounded cursor-pointer text-lg transition-transform duration-100 ease-out hover:bg-accent hover:scale-125 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none motion-reduce:hover:scale-100'
30
30
 
31
31
  export const emojiPickerSectionLabelStyles =
32
32
  'px-2 py-1 text-xs font-medium text-muted-foreground'
33
+
34
+ /** The swappable category/search/sticker panel; fades on category switch. */
35
+ export const emojiPickerPanelStyles =
36
+ 'motion-safe:animate-fade-in origin-top'
37
+
38
+ /** A single sticker cell in the sticker tab. */
39
+ export const emojiPickerStickerButtonStyles =
40
+ 'flex items-center justify-center rounded-lg p-1.5 aspect-square cursor-pointer transition-transform duration-100 ease-out hover:bg-accent hover:scale-110 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none motion-reduce:hover:scale-100'
@@ -9,11 +9,29 @@ export {
9
9
  EMOJI_DATA,
10
10
  EMOJI_CATEGORIES,
11
11
  CATEGORY_LABELS,
12
+ EMOJI_COUNT_TOTAL,
13
+ SHORTCODE_ALIASES,
12
14
  getAllEmojis,
15
+ resolveShortcode,
16
+ buildShortcodeMap,
17
+ searchEmojis,
13
18
  type EmojiCategory,
14
19
  type EmojiEntry,
15
20
  } from './emoji-data.js'
16
21
 
22
+ export {
23
+ twemojiFilename,
24
+ twemojiAssetUrl,
25
+ DEFAULT_TWEMOJI_BASE_URL,
26
+ } from './twemoji.js'
27
+
28
+ export {
29
+ STARTER_STICKER_SET,
30
+ type StickerItem,
31
+ type StickerSet,
32
+ type StickerKind,
33
+ } from './stickers.js'
34
+
17
35
  export {
18
36
  emojiPickerContainerStyles,
19
37
  emojiPickerSearchStyles,
@@ -22,4 +40,6 @@ export {
22
40
  emojiPickerGridStyles,
23
41
  emojiPickerEmojiButtonStyles,
24
42
  emojiPickerSectionLabelStyles,
43
+ emojiPickerPanelStyles,
44
+ emojiPickerStickerButtonStyles,
25
45
  } from './emoji-picker.styles.js'
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Sticker seam — types + a small bundled starter pack.
3
+ *
4
+ * Stickers are host-extensible: a consumer (or a future bundled pack) supplies
5
+ * `StickerSet`s and the picker renders them in a dedicated tab. Three render
6
+ * paths are supported so the API is ready for animated content without any
7
+ * heavy dependency in the core:
8
+ * - `kind: 'svg'` — inline SVG markup (can self-animate via SMIL, no JS).
9
+ * - `kind: 'image'` — a URL to a static or animated (WebP/GIF/APNG) asset.
10
+ * - `kind: 'lottie'`— a URL/JSON handed to a host-provided Lottie player.
11
+ *
12
+ * The starter pack below ships a handful of simple, consistent flat stickers
13
+ * (128×128, one visual language) so the tab is not empty out of the box, and
14
+ * proves the animated render path with a self-animating SMIL heart — no Lottie
15
+ * runtime required.
16
+ */
17
+
18
+ export type StickerKind = 'svg' | 'image' | 'lottie'
19
+
20
+ export interface StickerItem {
21
+ /** Stable id (unique within its set). */
22
+ id: string
23
+ /** Accessible label / alt text. */
24
+ label: string
25
+ kind: StickerKind
26
+ /**
27
+ * The sticker payload: inline SVG markup (`svg`), an asset URL (`image`), or
28
+ * a Lottie JSON URL or serialized object (`lottie`).
29
+ */
30
+ source: string
31
+ /** Search keywords. */
32
+ keywords?: string[]
33
+ /** True when the sticker animates (drives reduced-motion handling). */
34
+ animated?: boolean
35
+ }
36
+
37
+ export interface StickerSet {
38
+ /** Stable id (unique across sets). */
39
+ id: string
40
+ /** Human-readable set name (tab tooltip). */
41
+ label: string
42
+ /** A representative emoji/char for the set's tab icon. */
43
+ icon?: string
44
+ stickers: StickerItem[]
45
+ }
46
+
47
+ const svg = (inner: string): string =>
48
+ `<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">${inner}</svg>`
49
+
50
+ /**
51
+ * Refraction starter sticker pack — a tasteful, consistent flat set. The
52
+ * `pulse-heart` sticker self-animates via SMIL to demonstrate the animated
53
+ * render path; the rest are static.
54
+ */
55
+ export const STARTER_STICKER_SET: StickerSet = {
56
+ id: 'refraction-starter',
57
+ label: 'Refraction',
58
+ icon: '⭐',
59
+ stickers: [
60
+ {
61
+ id: 'pulse-heart',
62
+ label: 'Pulsing heart',
63
+ kind: 'svg',
64
+ animated: true,
65
+ keywords: ['heart', 'love', 'like'],
66
+ source: svg(
67
+ `<g fill="#ff5a7a"><path d="M64 112C34 90 16 72 16 50a26 26 0 0 1 48-14 26 26 0 0 1 48 14c0 22-18 40-48 62Z">` +
68
+ `<animateTransform attributeName="transform" type="scale" additive="sum" values="1;1.08;1" dur="1s" repeatCount="indefinite" calcMode="spline" keySplines="0.4 0 0.2 1;0.4 0 0.2 1" keyTimes="0;0.5;1"/>` +
69
+ `</path></g>`,
70
+ ),
71
+ },
72
+ {
73
+ id: 'star',
74
+ label: 'Gold star',
75
+ kind: 'svg',
76
+ keywords: ['star', 'favorite', 'award'],
77
+ source: svg(
78
+ `<path fill="#ffc93c" d="M64 14l14 30 33 4-24 23 6 33-29-16-29 16 6-33-24-23 33-4z"/>`,
79
+ ),
80
+ },
81
+ {
82
+ id: 'thumbs-up',
83
+ label: 'Thumbs up',
84
+ kind: 'svg',
85
+ keywords: ['thumbsup', 'like', 'yes', 'approve'],
86
+ source: svg(
87
+ `<g fill="#4c9aff"><rect x="20" y="56" width="20" height="50" rx="5"/>` +
88
+ `<path d="M48 56l16-34c9 0 14 7 12 18l-3 14h26c8 0 13 8 10 16l-9 26c-2 6-8 10-14 10H48Z"/></g>`,
89
+ ),
90
+ },
91
+ {
92
+ id: 'party',
93
+ label: 'Party popper',
94
+ kind: 'svg',
95
+ keywords: ['party', 'celebrate', 'tada', 'confetti'],
96
+ source: svg(
97
+ `<path fill="#ff8a3d" d="M18 110L52 44l32 32z"/>` +
98
+ `<circle cx="92" cy="30" r="6" fill="#ff5a7a"/><circle cx="110" cy="52" r="5" fill="#ffc93c"/>` +
99
+ `<circle cx="70" cy="20" r="5" fill="#4c9aff"/><circle cx="104" cy="82" r="6" fill="#5ad19a"/>`,
100
+ ),
101
+ },
102
+ {
103
+ id: 'smiley',
104
+ label: 'Smiley blob',
105
+ kind: 'svg',
106
+ keywords: ['happy', 'smile', 'face'],
107
+ source: svg(
108
+ `<circle cx="64" cy="64" r="46" fill="#ffd23f"/>` +
109
+ `<circle cx="48" cy="56" r="6" fill="#3a2d00"/><circle cx="80" cy="56" r="6" fill="#3a2d00"/>` +
110
+ `<path d="M44 78a24 24 0 0 0 40 0" stroke="#3a2d00" stroke-width="6" fill="none" stroke-linecap="round"/>`,
111
+ ),
112
+ },
113
+ {
114
+ id: 'fire',
115
+ label: 'Fire',
116
+ kind: 'svg',
117
+ keywords: ['fire', 'hot', 'lit'],
118
+ source: svg(
119
+ `<path fill="#ff6b35" d="M64 12c8 22-14 26-14 46a14 14 0 0 0 28 0c0-6-2-10-2-14 10 6 16 18 16 30a28 28 0 0 1-56 0c0-30 20-40 28-62Z"/>` +
120
+ `<path fill="#ffd23f" d="M64 62c4 8-6 10-6 20a10 10 0 0 0 20 0c0-8-8-12-14-20Z"/>`,
121
+ ),
122
+ },
123
+ {
124
+ id: 'check',
125
+ label: 'Check',
126
+ kind: 'svg',
127
+ keywords: ['check', 'done', 'ok', 'yes'],
128
+ source: svg(
129
+ `<circle cx="64" cy="64" r="46" fill="#5ad19a"/>` +
130
+ `<path d="M42 66l14 14 30-32" stroke="#fff" stroke-width="8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
131
+ ),
132
+ },
133
+ {
134
+ id: 'heart-eyes',
135
+ label: 'Love it',
136
+ kind: 'svg',
137
+ keywords: ['love', 'heart', 'adore'],
138
+ source: svg(
139
+ `<circle cx="64" cy="64" r="46" fill="#ffd23f"/>` +
140
+ `<path fill="#ff5a7a" d="M40 50c5-6 13-2 13 4 0 5-6 9-13 14-7-5-13-9-13-14 0-6 8-10 13-4Z"/>` +
141
+ `<path fill="#ff5a7a" d="M88 50c5-6 13-2 13 4 0 5-6 9-13 14-7-5-13-9-13-14 0-6 8-10 13-4Z"/>` +
142
+ `<path d="M44 84a24 24 0 0 0 40 0" stroke="#3a2d00" stroke-width="6" fill="none" stroke-linecap="round"/>`,
143
+ ),
144
+ },
145
+ ],
146
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Twemoji asset resolution — maps a native emoji glyph to its Twemoji SVG
3
+ * filename/URL. This is the data half of the `emojiRenderer` seam: it lets the
4
+ * React/Astro adapters render a UNIFORM emoji set (one look on every OS) instead
5
+ * of per-platform native glyphs, WhatsApp-style, by lazily loading one small SVG
6
+ * per glyph from a CDN (or a self-hosted mirror) — nothing is bundled eagerly.
7
+ *
8
+ * Twemoji graphics are © Twitter / the Twemoji contributors, licensed CC-BY 4.0.
9
+ * Consumers rendering with Twemoji must preserve that attribution (see the
10
+ * package README / NOTICE).
11
+ *
12
+ * The filename algorithm mirrors upstream `twemoji.toCodePoint`: surrogate
13
+ * pairs are combined to codepoints, and the U+FE0F variation selector is
14
+ * stripped from non-ZWJ sequences (Twemoji names files without it).
15
+ */
16
+
17
+ /** Default CDN base for Twemoji SVG assets (jsDelivr, pinned). */
18
+ export const DEFAULT_TWEMOJI_BASE_URL =
19
+ 'https://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/'
20
+
21
+ const ZWJ = '‍'
22
+ const VARIATION_SELECTOR_16 = /️/g
23
+
24
+ /** Combine surrogate pairs into dash-joined lowercase hex codepoints. */
25
+ function toCodePoint(input: string): string {
26
+ const parts: string[] = []
27
+ let pending = 0
28
+ let i = 0
29
+ while (i < input.length) {
30
+ const c = input.charCodeAt(i++)
31
+ if (pending) {
32
+ parts.push((0x10000 + ((pending - 0xd800) << 10) + (c - 0xdc00)).toString(16))
33
+ pending = 0
34
+ } else if (c >= 0xd800 && c <= 0xdbff) {
35
+ pending = c
36
+ } else {
37
+ parts.push(c.toString(16))
38
+ }
39
+ }
40
+ return parts.join('-')
41
+ }
42
+
43
+ /**
44
+ * Twemoji asset filename (without extension) for a glyph, e.g. `1f600`,
45
+ * `1f1fa-1f1f8`, `2764` (heart, FE0F stripped).
46
+ */
47
+ export function twemojiFilename(emoji: string): string {
48
+ // Keep FE0F only for ZWJ sequences, matching upstream Twemoji naming.
49
+ const normalized = emoji.indexOf(ZWJ) < 0 ? emoji.replace(VARIATION_SELECTOR_16, '') : emoji
50
+ return toCodePoint(normalized)
51
+ }
52
+
53
+ /** Full Twemoji SVG URL for a glyph. */
54
+ export function twemojiAssetUrl(
55
+ emoji: string,
56
+ baseUrl: string = DEFAULT_TWEMOJI_BASE_URL,
57
+ ): string {
58
+ const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`
59
+ return `${base}${twemojiFilename(emoji)}.svg`
60
+ }
@@ -1,127 +1,21 @@
1
1
  /**
2
2
  * Emoji system — shortcode to unicode mapping and search.
3
+ *
4
+ * The map is derived from the single shared emoji dataset in
5
+ * `@refraction-ui/emoji-picker` (`buildShortcodeMap`), so rich-editor, the
6
+ * emoji picker, and the composer's `:` trigger all resolve `:shortcode:` the
7
+ * same way — the ~120-entry hand-list that used to live here (and diverge from
8
+ * the picker) is gone. Popular aliases (`:smile:`, `:joy:`, `:100:`) live in
9
+ * the shared dataset's `SHORTCODE_ALIASES`.
3
10
  */
11
+ import { buildShortcodeMap } from '../emoji-picker/index.ts'
4
12
 
5
13
  // ---------------------------------------------------------------------------
6
14
  // Emoji map
7
15
  // ---------------------------------------------------------------------------
8
16
 
9
- export const EMOJI_MAP: Record<string, string> = {
10
- ':smile:': '\u{1F604}',
11
- ':laugh:': '\u{1F602}',
12
- ':joy:': '\u{1F602}',
13
- ':grin:': '\u{1F601}',
14
- ':wink:': '\u{1F609}',
15
- ':blush:': '\u{1F60A}',
16
- ':heart_eyes:': '\u{1F60D}',
17
- ':kissing:': '\u{1F617}',
18
- ':thinking:': '\u{1F914}',
19
- ':neutral:': '\u{1F610}',
20
- ':expressionless:': '\u{1F611}',
21
- ':unamused:': '\u{1F612}',
22
- ':sweat:': '\u{1F613}',
23
- ':pensive:': '\u{1F614}',
24
- ':confused:': '\u{1F615}',
25
- ':disappointed:': '\u{1F61E}',
26
- ':worried:': '\u{1F61F}',
27
- ':angry:': '\u{1F620}',
28
- ':rage:': '\u{1F621}',
29
- ':cry:': '\u{1F622}',
30
- ':sob:': '\u{1F62D}',
31
- ':scream:': '\u{1F631}',
32
- ':fearful:': '\u{1F628}',
33
- ':cold_sweat:': '\u{1F630}',
34
- ':relieved:': '\u{1F60C}',
35
- ':sleepy:': '\u{1F62A}',
36
- ':sleeping:': '\u{1F634}',
37
- ':mask:': '\u{1F637}',
38
- ':sunglasses:': '\u{1F60E}',
39
- ':nerd:': '\u{1F913}',
40
- ':heart:': '\u{2764}\u{FE0F}',
41
- ':broken_heart:': '\u{1F494}',
42
- ':sparkling_heart:': '\u{1F496}',
43
- ':two_hearts:': '\u{1F495}',
44
- ':fire:': '\u{1F525}',
45
- ':100:': '\u{1F4AF}',
46
- ':star:': '\u{2B50}',
47
- ':star2:': '\u{1F31F}',
48
- ':sparkles:': '\u{2728}',
49
- ':thumbsup:': '\u{1F44D}',
50
- ':thumbsdown:': '\u{1F44E}',
51
- ':wave:': '\u{1F44B}',
52
- ':clap:': '\u{1F44F}',
53
- ':raised_hands:': '\u{1F64C}',
54
- ':pray:': '\u{1F64F}',
55
- ':muscle:': '\u{1F4AA}',
56
- ':point_up:': '\u{261D}\u{FE0F}',
57
- ':point_down:': '\u{1F447}',
58
- ':point_left:': '\u{1F448}',
59
- ':point_right:': '\u{1F449}',
60
- ':ok_hand:': '\u{1F44C}',
61
- ':v:': '\u{270C}\u{FE0F}',
62
- ':eyes:': '\u{1F440}',
63
- ':tongue:': '\u{1F445}',
64
- ':lips:': '\u{1F444}',
65
- ':rocket:': '\u{1F680}',
66
- ':tada:': '\u{1F389}',
67
- ':confetti:': '\u{1F38A}',
68
- ':balloon:': '\u{1F388}',
69
- ':gift:': '\u{1F381}',
70
- ':trophy:': '\u{1F3C6}',
71
- ':medal:': '\u{1F3C5}',
72
- ':crown:': '\u{1F451}',
73
- ':gem:': '\u{1F48E}',
74
- ':bell:': '\u{1F514}',
75
- ':check:': '\u{2705}',
76
- ':x:': '\u{274C}',
77
- ':warning:': '\u{26A0}\u{FE0F}',
78
- ':no_entry:': '\u{26D4}',
79
- ':question:': '\u{2753}',
80
- ':exclamation:': '\u{2757}',
81
- ':bulb:': '\u{1F4A1}',
82
- ':book:': '\u{1F4D6}',
83
- ':memo:': '\u{1F4DD}',
84
- ':pencil:': '\u{270F}\u{FE0F}',
85
- ':calendar:': '\u{1F4C5}',
86
- ':clock:': '\u{1F550}',
87
- ':hourglass:': '\u{231B}',
88
- ':lock:': '\u{1F512}',
89
- ':key:': '\u{1F511}',
90
- ':hammer:': '\u{1F528}',
91
- ':wrench:': '\u{1F527}',
92
- ':link:': '\u{1F517}',
93
- ':paperclip:': '\u{1F4CE}',
94
- ':scissors:': '\u{2702}\u{FE0F}',
95
- ':package:': '\u{1F4E6}',
96
- ':email:': '\u{1F4E7}',
97
- ':phone:': '\u{1F4DE}',
98
- ':computer:': '\u{1F4BB}',
99
- ':house:': '\u{1F3E0}',
100
- ':tree:': '\u{1F333}',
101
- ':sun:': '\u{2600}\u{FE0F}',
102
- ':moon:': '\u{1F319}',
103
- ':cloud:': '\u{2601}\u{FE0F}',
104
- ':rain:': '\u{1F327}\u{FE0F}',
105
- ':snow:': '\u{2744}\u{FE0F}',
106
- ':zap:': '\u{26A1}',
107
- ':rainbow:': '\u{1F308}',
108
- ':pizza:': '\u{1F355}',
109
- ':hamburger:': '\u{1F354}',
110
- ':coffee:': '\u{2615}',
111
- ':beer:': '\u{1F37A}',
112
- ':wine:': '\u{1F377}',
113
- ':cake:': '\u{1F370}',
114
- ':dog:': '\u{1F436}',
115
- ':cat:': '\u{1F431}',
116
- ':poop:': '\u{1F4A9}',
117
- ':ghost:': '\u{1F47B}',
118
- ':skull:': '\u{1F480}',
119
- ':alien:': '\u{1F47D}',
120
- ':robot:': '\u{1F916}',
121
- ':handshake:': '\u{1F91D}',
122
- ':pray2:': '\u{1F64F}',
123
- ':salute:': '\u{1FAE1}',
124
- }
17
+ /** `:shortcode:` → glyph, built from the shared dataset (full set + aliases). */
18
+ export const EMOJI_MAP: Record<string, string> = buildShortcodeMap()
125
19
 
126
20
  // ---------------------------------------------------------------------------
127
21
  // Detection and search
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@refraction-ui/astro",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "All Refraction UI Astro components in one package",
5
5
  "type": "module",
6
6
  "exports": {