react-pebble 0.1.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.
- package/dist/lib/compiler.cjs +3 -0
- package/dist/lib/compiler.cjs.map +1 -0
- package/dist/lib/compiler.js +54 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/components.cjs +2 -0
- package/dist/lib/components.cjs.map +1 -0
- package/dist/lib/components.js +80 -0
- package/dist/lib/components.js.map +1 -0
- package/dist/lib/hooks.cjs +2 -0
- package/dist/lib/hooks.cjs.map +1 -0
- package/dist/lib/hooks.js +99 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/index.cjs +2 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.js +585 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/platform.cjs +2 -0
- package/dist/lib/platform.cjs.map +1 -0
- package/dist/lib/platform.js +52 -0
- package/dist/lib/platform.js.map +1 -0
- package/dist/lib/plugin.cjs +60 -0
- package/dist/lib/plugin.cjs.map +1 -0
- package/dist/lib/plugin.js +102 -0
- package/dist/lib/plugin.js.map +1 -0
- package/dist/lib/src/compiler/index.d.ts +40 -0
- package/dist/lib/src/components/index.d.ts +129 -0
- package/dist/lib/src/hooks/index.d.ts +75 -0
- package/dist/lib/src/index.d.ts +36 -0
- package/dist/lib/src/pebble-dom-shim.d.ts +45 -0
- package/dist/lib/src/pebble-dom.d.ts +59 -0
- package/dist/lib/src/pebble-output.d.ts +44 -0
- package/dist/lib/src/pebble-reconciler.d.ts +16 -0
- package/dist/lib/src/pebble-render.d.ts +31 -0
- package/dist/lib/src/platform.d.ts +30 -0
- package/dist/lib/src/plugin/index.d.ts +20 -0
- package/package.json +90 -0
- package/scripts/compile-to-piu.ts +1794 -0
- package/scripts/deploy.sh +46 -0
- package/src/compiler/index.ts +114 -0
- package/src/components/index.tsx +280 -0
- package/src/hooks/index.ts +311 -0
- package/src/index.ts +126 -0
- package/src/pebble-dom-shim.ts +266 -0
- package/src/pebble-dom.ts +190 -0
- package/src/pebble-output.ts +310 -0
- package/src/pebble-reconciler.ts +54 -0
- package/src/pebble-render.ts +311 -0
- package/src/platform.ts +50 -0
- package/src/plugin/index.ts +274 -0
- package/src/types/moddable.d.ts +156 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Deploy a react-pebble example to the Pebble Alloy emulator.
|
|
3
|
+
#
|
|
4
|
+
# Uses the Vite plugin to compile + scaffold .pebble-build/, then
|
|
5
|
+
# builds and installs to the emulator.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./scripts/deploy.sh watchface # compile + build + install + screenshot
|
|
9
|
+
# ./scripts/deploy.sh counter --logs # with live log streaming
|
|
10
|
+
# SETTLE_MS=200 ./scripts/deploy.sh jira-list # with async settle delay
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
EXAMPLE="${1:?Usage: deploy.sh <example-name> [--logs]}"
|
|
15
|
+
LOGS_FLAG=""
|
|
16
|
+
if [ "$2" = "--logs" ]; then LOGS_FLAG="--logs"; fi
|
|
17
|
+
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
19
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
20
|
+
BUILD_DIR="$PROJECT_DIR/.pebble-build"
|
|
21
|
+
|
|
22
|
+
echo "=== Compiling + scaffolding $EXAMPLE ==="
|
|
23
|
+
cd "$PROJECT_DIR"
|
|
24
|
+
ENTRY="examples/${EXAMPLE}.tsx" npx vite build --config vite.config.plugin-test.js 2>&1 | grep "\[react-pebble\]"
|
|
25
|
+
|
|
26
|
+
echo "=== Building ==="
|
|
27
|
+
cd "$BUILD_DIR"
|
|
28
|
+
pebble build 2>&1 | tail -5
|
|
29
|
+
|
|
30
|
+
echo "=== Installing to emery emulator ==="
|
|
31
|
+
pebble kill >/dev/null 2>&1 || true
|
|
32
|
+
pebble wipe >/dev/null 2>&1 || true
|
|
33
|
+
sleep 2
|
|
34
|
+
|
|
35
|
+
if [ -n "$LOGS_FLAG" ]; then
|
|
36
|
+
pebble install --emulator emery --logs
|
|
37
|
+
else
|
|
38
|
+
pebble install --emulator emery --logs > /tmp/react-pebble-emu.log 2>&1 &
|
|
39
|
+
EMU_PID=$!
|
|
40
|
+
sleep 10
|
|
41
|
+
pebble screenshot "/tmp/react-pebble-${EXAMPLE}.png" 2>&1 | tail -1
|
|
42
|
+
echo "=== Screenshot saved to /tmp/react-pebble-${EXAMPLE}.png ==="
|
|
43
|
+
pebble kill >/dev/null 2>&1 || true
|
|
44
|
+
kill $EMU_PID 2>/dev/null || true
|
|
45
|
+
wait $EMU_PID 2>/dev/null || true
|
|
46
|
+
fi
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/compiler/index.ts — react-pebble compile-to-piu library API.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the compile-to-piu.ts script as a programmatic API. The script
|
|
5
|
+
* runs as a subprocess (it uses module-level state that requires process
|
|
6
|
+
* isolation). A future refactoring will inline the logic as a pure function.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { compileToPiu } from 'react-pebble/compiler';
|
|
10
|
+
* const result = await compileToPiu({ entry: 'examples/watchface.tsx' });
|
|
11
|
+
* console.log(result.code); // piu Application.template JS
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { execSync } from 'node:child_process';
|
|
15
|
+
import { resolve, dirname, basename } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
|
|
21
|
+
export interface CompileOptions {
|
|
22
|
+
/** Path to the entry .tsx file (relative to cwd or absolute) */
|
|
23
|
+
entry: string;
|
|
24
|
+
/** Milliseconds to wait for async effects (useEffect/setTimeout) */
|
|
25
|
+
settleMs?: number;
|
|
26
|
+
/** Target platform (default: 'emery') */
|
|
27
|
+
platform?: string;
|
|
28
|
+
/** Logger for diagnostic messages */
|
|
29
|
+
logger?: (msg: string) => void;
|
|
30
|
+
/** Project root directory (default: process.cwd()) */
|
|
31
|
+
projectRoot?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CompileResult {
|
|
35
|
+
/** The compiled piu JavaScript code */
|
|
36
|
+
code: string;
|
|
37
|
+
/** Whether the component uses useButton (needs watchapp mode) */
|
|
38
|
+
hasButtons: boolean;
|
|
39
|
+
/** Message keys used by useMessage hooks */
|
|
40
|
+
messageKeys: string[];
|
|
41
|
+
/** Diagnostic messages from the compiler */
|
|
42
|
+
diagnostics: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compile a Preact component to piu Application.template code.
|
|
47
|
+
*
|
|
48
|
+
* Internally runs scripts/compile-to-piu.ts as a subprocess.
|
|
49
|
+
*/
|
|
50
|
+
export async function compileToPiu(options: CompileOptions): Promise<CompileResult> {
|
|
51
|
+
const log = options.logger ?? (() => {});
|
|
52
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
53
|
+
|
|
54
|
+
// Resolve the entry to an example name (for backwards compat with the script)
|
|
55
|
+
const entryPath = resolve(projectRoot, options.entry);
|
|
56
|
+
const exampleName = basename(entryPath).replace(/\.[jt]sx?$/, '');
|
|
57
|
+
|
|
58
|
+
// Find the compiler script
|
|
59
|
+
const scriptPath = resolve(__dirname, '../../scripts/compile-to-piu.ts');
|
|
60
|
+
if (!existsSync(scriptPath)) {
|
|
61
|
+
throw new Error(`Compiler script not found at ${scriptPath}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
log(`Compiling ${exampleName}...`);
|
|
65
|
+
|
|
66
|
+
const env: Record<string, string> = {
|
|
67
|
+
...process.env as Record<string, string>,
|
|
68
|
+
EXAMPLE: exampleName,
|
|
69
|
+
};
|
|
70
|
+
if (options.settleMs) {
|
|
71
|
+
env.SETTLE_MS = String(options.settleMs);
|
|
72
|
+
}
|
|
73
|
+
if (options.platform) {
|
|
74
|
+
env.PEBBLE_PLATFORM = options.platform;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Run the compiler script and capture stdout (code) + stderr (diagnostics)
|
|
78
|
+
let code: string;
|
|
79
|
+
let diagnostics: string;
|
|
80
|
+
try {
|
|
81
|
+
code = execSync(`npx tsx "${scriptPath}"`, {
|
|
82
|
+
cwd: projectRoot,
|
|
83
|
+
env,
|
|
84
|
+
encoding: 'utf-8',
|
|
85
|
+
timeout: 30000,
|
|
86
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
87
|
+
});
|
|
88
|
+
// Re-run to capture stderr separately (execSync doesn't give both easily)
|
|
89
|
+
try {
|
|
90
|
+
diagnostics = execSync(`npx tsx "${scriptPath}" 2>&1 1>/dev/null`, {
|
|
91
|
+
cwd: projectRoot,
|
|
92
|
+
env,
|
|
93
|
+
encoding: 'utf-8',
|
|
94
|
+
timeout: 30000,
|
|
95
|
+
});
|
|
96
|
+
} catch {
|
|
97
|
+
diagnostics = '';
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
const e = err as { stderr?: string; message?: string };
|
|
101
|
+
throw new Error(`Compilation failed for ${exampleName}: ${e.stderr ?? e.message}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Parse diagnostics to extract metadata
|
|
105
|
+
const hasButtons = diagnostics.includes('Button bindings discovered:') &&
|
|
106
|
+
!diagnostics.includes('Button bindings discovered: 0');
|
|
107
|
+
const messageKeys: string[] = [];
|
|
108
|
+
const msgMatch = diagnostics.match(/useMessage detected: key="([^"]+)"/);
|
|
109
|
+
if (msgMatch?.[1]) messageKeys.push(msgMatch[1]);
|
|
110
|
+
|
|
111
|
+
log(`Compiled ${exampleName}: ${code.split('\n').length} lines, buttons=${hasButtons}, messageKeys=[${messageKeys.join(',')}]`);
|
|
112
|
+
|
|
113
|
+
return { code, hasButtons, messageKeys, diagnostics };
|
|
114
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* components/index.tsx — React component wrappers for Pebble primitives
|
|
3
|
+
*
|
|
4
|
+
* These provide a friendly JSX API that maps to the underlying
|
|
5
|
+
* pbl-* element types the reconciler handles.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { h } from 'preact';
|
|
9
|
+
import type { ComponentChildren } from 'preact';
|
|
10
|
+
import type { PebbleButtonHandler } from '../hooks/index.js';
|
|
11
|
+
|
|
12
|
+
// Use Preact's `h` as a stand-in for `React.createElement` so we can keep
|
|
13
|
+
// the factory-based element construction that was in the react version.
|
|
14
|
+
const React = { createElement: h } as const;
|
|
15
|
+
|
|
16
|
+
// Unify ReactNode with Preact's ComponentChildren so we don't have to
|
|
17
|
+
// rename every prop type.
|
|
18
|
+
type ReactNode = ComponentChildren;
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Shared prop types
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
export interface PositionProps {
|
|
25
|
+
x?: number;
|
|
26
|
+
y?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SizeProps {
|
|
30
|
+
w?: number;
|
|
31
|
+
h?: number;
|
|
32
|
+
/** Alias for `w`. */
|
|
33
|
+
width?: number;
|
|
34
|
+
/** Alias for `h`. */
|
|
35
|
+
height?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ButtonHandlerProps {
|
|
39
|
+
onUp?: PebbleButtonHandler;
|
|
40
|
+
onDown?: PebbleButtonHandler;
|
|
41
|
+
onSelect?: PebbleButtonHandler;
|
|
42
|
+
onBack?: PebbleButtonHandler;
|
|
43
|
+
onLongUp?: PebbleButtonHandler;
|
|
44
|
+
onLongDown?: PebbleButtonHandler;
|
|
45
|
+
onLongSelect?: PebbleButtonHandler;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type ColorName =
|
|
49
|
+
| 'black' | 'white' | 'red' | 'green' | 'blue' | 'yellow' | 'orange'
|
|
50
|
+
| 'cyan' | 'magenta' | 'clear' | 'lightGray' | 'darkGray'
|
|
51
|
+
// Pass-through for raw GColor / hex values
|
|
52
|
+
| (string & {});
|
|
53
|
+
|
|
54
|
+
export type FontName =
|
|
55
|
+
| 'gothic14' | 'gothic14Bold' | 'gothic18' | 'gothic18Bold'
|
|
56
|
+
| 'gothic24' | 'gothic24Bold' | 'gothic28' | 'gothic28Bold'
|
|
57
|
+
| 'bitham30Black' | 'bitham42Bold' | 'bitham42Light'
|
|
58
|
+
| 'bitham34MediumNumbers' | 'bitham42MediumNumbers'
|
|
59
|
+
| 'robotoCondensed21' | 'roboto21' | 'droid28'
|
|
60
|
+
| 'leco20' | 'leco26' | 'leco28' | 'leco32' | 'leco36' | 'leco38' | 'leco42'
|
|
61
|
+
| (string & {});
|
|
62
|
+
|
|
63
|
+
export type Alignment = 'left' | 'center' | 'right';
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// JSX intrinsic element declarations for the pbl-* tags
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
declare module 'preact' {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
71
|
+
namespace JSX {
|
|
72
|
+
interface IntrinsicElements {
|
|
73
|
+
'pbl-root': IntrinsicPblProps;
|
|
74
|
+
'pbl-rect': IntrinsicPblProps;
|
|
75
|
+
'pbl-circle': IntrinsicPblProps;
|
|
76
|
+
'pbl-text': IntrinsicPblProps;
|
|
77
|
+
'pbl-line': IntrinsicPblProps;
|
|
78
|
+
'pbl-image': IntrinsicPblProps;
|
|
79
|
+
'pbl-group': IntrinsicPblProps;
|
|
80
|
+
'pbl-statusbar': IntrinsicPblProps;
|
|
81
|
+
'pbl-actionbar': IntrinsicPblProps;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface IntrinsicPblProps {
|
|
87
|
+
children?: ReactNode;
|
|
88
|
+
// Loose escape hatch — wrappers below provide the typed surface.
|
|
89
|
+
[key: string]: unknown;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Layout primitives
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
export interface WindowProps extends ButtonHandlerProps {
|
|
97
|
+
backgroundColor?: ColorName;
|
|
98
|
+
fullscreen?: boolean;
|
|
99
|
+
children?: ReactNode;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function Window({ children, ...props }: WindowProps) {
|
|
103
|
+
return React.createElement('pbl-group', props, children);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface RectProps extends PositionProps, SizeProps {
|
|
107
|
+
fill?: ColorName;
|
|
108
|
+
stroke?: ColorName;
|
|
109
|
+
strokeWidth?: number;
|
|
110
|
+
children?: ReactNode;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function Rect({ children, ...props }: RectProps) {
|
|
114
|
+
return React.createElement('pbl-rect', props, children);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface CircleProps extends PositionProps {
|
|
118
|
+
r?: number;
|
|
119
|
+
/** Alias for `r`. */
|
|
120
|
+
radius?: number;
|
|
121
|
+
fill?: ColorName;
|
|
122
|
+
stroke?: ColorName;
|
|
123
|
+
strokeWidth?: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function Circle(props: CircleProps) {
|
|
127
|
+
return React.createElement('pbl-circle', props);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface TextProps extends PositionProps, SizeProps {
|
|
131
|
+
font?: FontName;
|
|
132
|
+
color?: ColorName;
|
|
133
|
+
align?: Alignment;
|
|
134
|
+
children?: ReactNode;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function Text({ children, ...props }: TextProps) {
|
|
138
|
+
return React.createElement('pbl-text', props, children);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface LineProps extends PositionProps {
|
|
142
|
+
x2?: number;
|
|
143
|
+
y2?: number;
|
|
144
|
+
color?: ColorName;
|
|
145
|
+
strokeWidth?: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function Line(props: LineProps) {
|
|
149
|
+
return React.createElement('pbl-line', props);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface ImageProps extends PositionProps {
|
|
153
|
+
bitmap: unknown;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function Image(props: ImageProps) {
|
|
157
|
+
return React.createElement('pbl-image', props);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface GroupProps extends PositionProps {
|
|
161
|
+
children?: ReactNode;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function Group({ children, ...props }: GroupProps) {
|
|
165
|
+
return React.createElement('pbl-group', props, children);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface StatusBarProps {
|
|
169
|
+
color?: ColorName;
|
|
170
|
+
backgroundColor?: ColorName;
|
|
171
|
+
separator?: 'dotted' | 'line' | 'none';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function StatusBar(props: StatusBarProps) {
|
|
175
|
+
return React.createElement('pbl-statusbar', props);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface ActionBarProps {
|
|
179
|
+
upIcon?: unknown;
|
|
180
|
+
selectIcon?: unknown;
|
|
181
|
+
downIcon?: unknown;
|
|
182
|
+
backgroundColor?: ColorName;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function ActionBar(props: ActionBarProps) {
|
|
186
|
+
return React.createElement('pbl-actionbar', props);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// Convenience composites
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
export interface CardProps extends PositionProps {
|
|
194
|
+
title: ReactNode;
|
|
195
|
+
body?: ReactNode;
|
|
196
|
+
titleFont?: FontName;
|
|
197
|
+
bodyFont?: FontName;
|
|
198
|
+
w?: number;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function Card({
|
|
202
|
+
title,
|
|
203
|
+
body,
|
|
204
|
+
titleFont,
|
|
205
|
+
bodyFont,
|
|
206
|
+
x = 0,
|
|
207
|
+
y = 0,
|
|
208
|
+
w = 144,
|
|
209
|
+
...props
|
|
210
|
+
}: CardProps) {
|
|
211
|
+
const titleH = 28;
|
|
212
|
+
const bodyY = titleH + 4;
|
|
213
|
+
|
|
214
|
+
return React.createElement(
|
|
215
|
+
'pbl-group',
|
|
216
|
+
{ x, y, ...props },
|
|
217
|
+
React.createElement('pbl-rect', { x: 0, y: 0, w, h: titleH, fill: 'white' }),
|
|
218
|
+
React.createElement(
|
|
219
|
+
'pbl-text',
|
|
220
|
+
{
|
|
221
|
+
x: 4,
|
|
222
|
+
y: 2,
|
|
223
|
+
w: w - 8,
|
|
224
|
+
h: titleH,
|
|
225
|
+
font: titleFont ?? 'gothic18Bold',
|
|
226
|
+
color: 'black',
|
|
227
|
+
},
|
|
228
|
+
title,
|
|
229
|
+
),
|
|
230
|
+
body
|
|
231
|
+
? React.createElement(
|
|
232
|
+
'pbl-text',
|
|
233
|
+
{
|
|
234
|
+
x: 4,
|
|
235
|
+
y: bodyY,
|
|
236
|
+
w: w - 8,
|
|
237
|
+
h: 120,
|
|
238
|
+
font: bodyFont ?? 'gothic14',
|
|
239
|
+
color: 'white',
|
|
240
|
+
},
|
|
241
|
+
body,
|
|
242
|
+
)
|
|
243
|
+
: null,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface BadgeProps extends PositionProps {
|
|
248
|
+
r?: number;
|
|
249
|
+
color?: ColorName;
|
|
250
|
+
textColor?: ColorName;
|
|
251
|
+
children?: ReactNode;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function Badge({
|
|
255
|
+
x = 0,
|
|
256
|
+
y = 0,
|
|
257
|
+
r = 12,
|
|
258
|
+
color = 'red',
|
|
259
|
+
textColor = 'white',
|
|
260
|
+
children,
|
|
261
|
+
}: BadgeProps) {
|
|
262
|
+
return React.createElement(
|
|
263
|
+
'pbl-group',
|
|
264
|
+
{ x, y },
|
|
265
|
+
React.createElement('pbl-circle', { x: 0, y: 0, r, fill: color }),
|
|
266
|
+
React.createElement(
|
|
267
|
+
'pbl-text',
|
|
268
|
+
{
|
|
269
|
+
x: 0,
|
|
270
|
+
y: r - 8,
|
|
271
|
+
w: r * 2,
|
|
272
|
+
h: 16,
|
|
273
|
+
font: 'gothic14Bold',
|
|
274
|
+
color: textColor,
|
|
275
|
+
align: 'center',
|
|
276
|
+
},
|
|
277
|
+
children,
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
}
|