@blobz/react 0.0.0-stage → 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/README.md CHANGED
@@ -1,3 +1,66 @@
1
- # Temporary Holding Version
1
+ # @blobz/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The React component library for Blobz. It renders deterministic, name-generated
4
+ avatars and explicit custom faces.
5
+
6
+ ## Usage
7
+
8
+ ```tsx
9
+ import { Avatar } from "@blobz/react";
10
+
11
+ export function Profile() {
12
+ return (
13
+ <Avatar
14
+ name="Ada Lovelace"
15
+ palette={["#f2f1f4", "#7c6cf2"]}
16
+ frame="rounded"
17
+ size={64}
18
+ />
19
+ );
20
+ }
21
+ ```
22
+
23
+ The component forwards standard image props and its ref to the underlying
24
+ `HTMLImageElement`:
25
+
26
+ ```tsx
27
+ <Avatar
28
+ name={user.id}
29
+ alt={`${user.name}'s avatar`}
30
+ className="rounded-xl"
31
+ loading="lazy"
32
+ size="3rem"
33
+ />
34
+ ```
35
+
36
+ Pass an empty `alt` string when the image is decorative.
37
+
38
+ ## Custom faces
39
+
40
+ `CustomAvatar` renders a complete semantic definition without using a name to
41
+ choose any facial feature:
42
+
43
+ ```tsx
44
+ import { CustomAvatar } from "@blobz/react/custom";
45
+
46
+ const definition = {
47
+ type: "custom",
48
+ version: 1,
49
+ frame: "circle",
50
+ palette: ["#ffffff", "#12b76a"],
51
+ choices: {
52
+ shape: "leaf",
53
+ eyes: "distorted",
54
+ look: "centre",
55
+ eyeSpacing: "medium",
56
+ mouth: "wide smile",
57
+ brows: "quizzical",
58
+ mirrored: false,
59
+ faceAngle: 2,
60
+ bodyTilt: -4,
61
+ facePosition: { x: 1, y: 0 },
62
+ },
63
+ } as const;
64
+
65
+ <CustomAvatar definition={definition} size={64} />;
66
+ ```
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithRef, CSSProperties } from 'react';
3
+ import { AvatarChoiceOverrides, AvatarFrame, AvatarPalette } from '@blobz/avatar-core';
4
+
5
+ type AvatarSize = CSSProperties["width"];
6
+ declare const createAvatarDataUri: (svg: string) => string;
7
+ interface AvatarProps extends Omit<ComponentPropsWithRef<"img">, "alt" | "children" | "height" | "src" | "width"> {
8
+ /** Accessible image description. Pass an empty string for a decorative avatar. */
9
+ readonly alt?: string;
10
+ /** Zero-based selections used to pin individual facial features. */
11
+ readonly choices?: AvatarChoiceOverrides | null;
12
+ /** Shape of the avatar's outer crop. */
13
+ readonly frame?: AvatarFrame;
14
+ /** Stable value used to deterministically generate the face. */
15
+ readonly name: string | number;
16
+ /** Background and body colours, in that order. */
17
+ readonly palette?: AvatarPalette;
18
+ /** Square rendered size. Defaults to 80 pixels. */
19
+ readonly size?: AvatarSize;
20
+ }
21
+ declare function Avatar({ alt, choices, decoding, draggable, frame, name, palette, ref, size, style, ...imageProps }: AvatarProps): react.JSX.Element;
22
+
23
+ export { Avatar, type AvatarProps, type AvatarSize, createAvatarDataUri };
package/dist/avatar.js ADDED
@@ -0,0 +1,46 @@
1
+ import {
2
+ AvatarImage
3
+ } from "./chunk-DU7BH2AL.js";
4
+
5
+ // src/avatar.tsx
6
+ import {
7
+ DEFAULT_AVATAR_PALETTE,
8
+ makeAvatar
9
+ } from "@blobz/avatar-core";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var createAvatarDataUri = (svg) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
12
+ function Avatar({
13
+ alt,
14
+ choices = null,
15
+ decoding = "async",
16
+ draggable = false,
17
+ frame = "rounded",
18
+ name,
19
+ palette = DEFAULT_AVATAR_PALETTE,
20
+ ref,
21
+ size = 80,
22
+ style,
23
+ ...imageProps
24
+ }) {
25
+ const avatar = makeAvatar(name, palette, frame, choices);
26
+ return /* @__PURE__ */ jsx(
27
+ AvatarImage,
28
+ {
29
+ ...imageProps,
30
+ alt: alt ?? `Blob avatar for ${avatar.key}`,
31
+ decoding,
32
+ draggable,
33
+ fallbackAlt: `Blob avatar for ${avatar.key}`,
34
+ ref,
35
+ size,
36
+ source: "generated",
37
+ style,
38
+ svg: avatar.svg
39
+ }
40
+ );
41
+ }
42
+ export {
43
+ Avatar,
44
+ createAvatarDataUri
45
+ };
46
+ //# sourceMappingURL=avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/avatar.tsx"],"sourcesContent":["import {\n\ttype AvatarChoiceOverrides,\n\ttype AvatarFrame,\n\ttype AvatarPalette,\n\ttype AvatarResult,\n\tDEFAULT_AVATAR_PALETTE,\n\tmakeAvatar,\n} from \"@blobz/avatar-core\";\nimport type { ComponentPropsWithRef, CSSProperties } from \"react\";\nimport { AvatarImage } from \"./avatar-image\";\n\nexport type AvatarSize = CSSProperties[\"width\"];\n\nexport const createAvatarDataUri = (svg: string): string =>\n\t`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;\n\nexport interface AvatarProps\n\textends Omit<\n\t\tComponentPropsWithRef<\"img\">,\n\t\t\"alt\" | \"children\" | \"height\" | \"src\" | \"width\"\n\t> {\n\t/** Accessible image description. Pass an empty string for a decorative avatar. */\n\treadonly alt?: string;\n\t/** Zero-based selections used to pin individual facial features. */\n\treadonly choices?: AvatarChoiceOverrides | null;\n\t/** Shape of the avatar's outer crop. */\n\treadonly frame?: AvatarFrame;\n\t/** Stable value used to deterministically generate the face. */\n\treadonly name: string | number;\n\t/** Background and body colours, in that order. */\n\treadonly palette?: AvatarPalette;\n\t/** Square rendered size. Defaults to 80 pixels. */\n\treadonly size?: AvatarSize;\n}\n\nexport function Avatar({\n\talt,\n\tchoices = null,\n\tdecoding = \"async\",\n\tdraggable = false,\n\tframe = \"rounded\",\n\tname,\n\tpalette = DEFAULT_AVATAR_PALETTE,\n\tref,\n\tsize = 80,\n\tstyle,\n\t...imageProps\n}: AvatarProps) {\n\tconst avatar: AvatarResult = makeAvatar(name, palette, frame, choices);\n\n\treturn (\n\t\t<AvatarImage\n\t\t\t{...imageProps}\n\t\t\talt={alt ?? `Blob avatar for ${avatar.key}`}\n\t\t\tdecoding={decoding}\n\t\t\tdraggable={draggable}\n\t\t\tfallbackAlt={`Blob avatar for ${avatar.key}`}\n\t\t\tref={ref}\n\t\t\tsize={size}\n\t\t\tsource=\"generated\"\n\t\t\tstyle={style}\n\t\t\tsvg={avatar.svg}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;AAAA;AAAA,EAKC;AAAA,EACA;AAAA,OACM;AA4CL;AAtCK,IAAM,sBAAsB,CAAC,QACnC,oCAAoC,mBAAmB,GAAG,CAAC;AAqBrD,SAAS,OAAO;AAAA,EACtB;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,GAAG;AACJ,GAAgB;AACf,QAAM,SAAuB,WAAW,MAAM,SAAS,OAAO,OAAO;AAErE,SACC;AAAA,IAAC;AAAA;AAAA,MACC,GAAG;AAAA,MACJ,KAAK,OAAO,mBAAmB,OAAO,GAAG;AAAA,MACzC;AAAA,MACA;AAAA,MACA,aAAa,mBAAmB,OAAO,GAAG;AAAA,MAC1C;AAAA,MACA;AAAA,MACA,QAAO;AAAA,MACP;AAAA,MACA,KAAK,OAAO;AAAA;AAAA,EACb;AAEF;","names":[]}
@@ -0,0 +1,41 @@
1
+ // src/avatar-image.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ var createAvatarDataUri = (svg) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
4
+ function AvatarImage({
5
+ alt,
6
+ decoding = "async",
7
+ draggable = false,
8
+ fallbackAlt,
9
+ ref,
10
+ size = 80,
11
+ source,
12
+ style,
13
+ svg,
14
+ ...imageProps
15
+ }) {
16
+ const numericSize = typeof size === "number" ? size : void 0;
17
+ return (
18
+ // biome-ignore lint/performance/noImgElement: Blobz renders an in-memory SVG data URI that image optimizers cannot improve.
19
+ /* @__PURE__ */ jsx(
20
+ "img",
21
+ {
22
+ ...imageProps,
23
+ alt: alt ?? fallbackAlt,
24
+ "data-blobz-avatar": "",
25
+ "data-blobz-source": source,
26
+ decoding,
27
+ draggable,
28
+ height: numericSize,
29
+ ref,
30
+ src: createAvatarDataUri(svg),
31
+ style: { height: size, width: size, ...style },
32
+ width: numericSize
33
+ }
34
+ )
35
+ );
36
+ }
37
+
38
+ export {
39
+ AvatarImage
40
+ };
41
+ //# sourceMappingURL=chunk-DU7BH2AL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/avatar-image.tsx"],"sourcesContent":["import type { ComponentPropsWithRef, CSSProperties } from \"react\";\n\nexport type AvatarSize = CSSProperties[\"width\"];\n\nexport interface AvatarImageProps\n\textends Omit<\n\t\tComponentPropsWithRef<\"img\">,\n\t\t\"alt\" | \"children\" | \"height\" | \"src\" | \"width\"\n\t> {\n\treadonly alt?: string;\n\treadonly fallbackAlt: string;\n\treadonly size?: AvatarSize;\n\treadonly source: \"custom\" | \"generated\";\n\treadonly svg: string;\n}\n\nexport const createAvatarDataUri = (svg: string): string =>\n\t`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;\n\nexport function AvatarImage({\n\talt,\n\tdecoding = \"async\",\n\tdraggable = false,\n\tfallbackAlt,\n\tref,\n\tsize = 80,\n\tsource,\n\tstyle,\n\tsvg,\n\t...imageProps\n}: AvatarImageProps) {\n\tconst numericSize = typeof size === \"number\" ? size : undefined;\n\n\treturn (\n\t\t// biome-ignore lint/performance/noImgElement: Blobz renders an in-memory SVG data URI that image optimizers cannot improve.\n\t\t<img\n\t\t\t{...imageProps}\n\t\t\talt={alt ?? fallbackAlt}\n\t\t\tdata-blobz-avatar=\"\"\n\t\t\tdata-blobz-source={source}\n\t\t\tdecoding={decoding}\n\t\t\tdraggable={draggable}\n\t\t\theight={numericSize}\n\t\t\tref={ref}\n\t\t\tsrc={createAvatarDataUri(svg)}\n\t\t\tstyle={{ height: size, width: size, ...style }}\n\t\t\twidth={numericSize}\n\t\t/>\n\t);\n}\n"],"mappings":";AAmCE;AAnBK,IAAM,sBAAsB,CAAC,QACnC,oCAAoC,mBAAmB,GAAG,CAAC;AAErD,SAAS,YAAY;AAAA,EAC3B;AAAA,EACA,WAAW;AAAA,EACX,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACJ,GAAqB;AACpB,QAAM,cAAc,OAAO,SAAS,WAAW,OAAO;AAEtD;AAAA;AAAA,IAEC;AAAA,MAAC;AAAA;AAAA,QACC,GAAG;AAAA,QACJ,KAAK,OAAO;AAAA,QACZ,qBAAkB;AAAA,QAClB,qBAAmB;AAAA,QACnB;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA,KAAK,oBAAoB,GAAG;AAAA,QAC5B,OAAO,EAAE,QAAQ,MAAM,OAAO,MAAM,GAAG,MAAM;AAAA,QAC7C,OAAO;AAAA;AAAA,IACR;AAAA;AAEF;","names":[]}
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties, ComponentPropsWithRef } from 'react';
3
+ import { CustomAvatarDefinition } from '@blobz/avatar-core';
4
+
5
+ type AvatarSize = CSSProperties["width"];
6
+
7
+ type CustomAvatarValue = CustomAvatarDefinition;
8
+ interface CustomAvatarProps extends Omit<ComponentPropsWithRef<"img">, "alt" | "children" | "height" | "src" | "width"> {
9
+ /** Accessible image description. Pass an empty string for a decorative avatar. */
10
+ readonly alt?: string;
11
+ /** Complete, serializable face definition. */
12
+ readonly definition: CustomAvatarDefinition;
13
+ /** Square rendered size. Defaults to 80 pixels. */
14
+ readonly size?: AvatarSize;
15
+ }
16
+ declare function CustomAvatar({ alt, decoding, definition, draggable, ref, size, style, ...imageProps }: CustomAvatarProps): react.JSX.Element;
17
+
18
+ export { CustomAvatar, type CustomAvatarProps, type CustomAvatarValue };
@@ -0,0 +1,40 @@
1
+ import {
2
+ AvatarImage
3
+ } from "./chunk-DU7BH2AL.js";
4
+
5
+ // src/custom-avatar.tsx
6
+ import {
7
+ createCustomAvatar
8
+ } from "@blobz/avatar-core";
9
+ import { jsx } from "react/jsx-runtime";
10
+ function CustomAvatar({
11
+ alt,
12
+ decoding = "async",
13
+ definition,
14
+ draggable = false,
15
+ ref,
16
+ size = 80,
17
+ style,
18
+ ...imageProps
19
+ }) {
20
+ const avatar = createCustomAvatar(definition);
21
+ return /* @__PURE__ */ jsx(
22
+ AvatarImage,
23
+ {
24
+ ...imageProps,
25
+ alt,
26
+ decoding,
27
+ draggable,
28
+ fallbackAlt: "Custom blob avatar",
29
+ ref,
30
+ size,
31
+ source: "custom",
32
+ style,
33
+ svg: avatar.svg
34
+ }
35
+ );
36
+ }
37
+ export {
38
+ CustomAvatar
39
+ };
40
+ //# sourceMappingURL=custom-avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/custom-avatar.tsx"],"sourcesContent":["import {\n\ttype CustomAvatarDefinition,\n\ttype CustomAvatarResult,\n\tcreateCustomAvatar,\n} from \"@blobz/avatar-core\";\nimport type { ComponentPropsWithRef } from \"react\";\nimport { AvatarImage, type AvatarSize } from \"./avatar-image\";\n\nexport type CustomAvatarValue = CustomAvatarDefinition;\n\nexport interface CustomAvatarProps\n\textends Omit<\n\t\tComponentPropsWithRef<\"img\">,\n\t\t\"alt\" | \"children\" | \"height\" | \"src\" | \"width\"\n\t> {\n\t/** Accessible image description. Pass an empty string for a decorative avatar. */\n\treadonly alt?: string;\n\t/** Complete, serializable face definition. */\n\treadonly definition: CustomAvatarDefinition;\n\t/** Square rendered size. Defaults to 80 pixels. */\n\treadonly size?: AvatarSize;\n}\n\nexport function CustomAvatar({\n\talt,\n\tdecoding = \"async\",\n\tdefinition,\n\tdraggable = false,\n\tref,\n\tsize = 80,\n\tstyle,\n\t...imageProps\n}: CustomAvatarProps) {\n\tconst avatar: CustomAvatarResult = createCustomAvatar(definition);\n\n\treturn (\n\t\t<AvatarImage\n\t\t\t{...imageProps}\n\t\t\talt={alt}\n\t\t\tdecoding={decoding}\n\t\t\tdraggable={draggable}\n\t\t\tfallbackAlt=\"Custom blob avatar\"\n\t\t\tref={ref}\n\t\t\tsize={size}\n\t\t\tsource=\"custom\"\n\t\t\tstyle={style}\n\t\t\tsvg={avatar.svg}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;AAAA;AAAA,EAGC;AAAA,OACM;AAgCL;AAbK,SAAS,aAAa;AAAA,EAC5B;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,GAAG;AACJ,GAAsB;AACrB,QAAM,SAA6B,mBAAmB,UAAU;AAEhE,SACC;AAAA,IAAC;AAAA;AAAA,MACC,GAAG;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,QAAO;AAAA,MACP;AAAA,MACA,KAAK,OAAO;AAAA;AAAA,EACb;AAEF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@blobz/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "React components for generated and custom Blobz avatars.",
5
+ "keywords": [
6
+ "avatar",
7
+ "blob",
8
+ "react",
9
+ "svg"
10
+ ],
11
+ "homepage": "https://blobz.lirena.in",
12
+ "bugs": {
13
+ "url": "https://github.com/lirena00/blobz/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/lirena00/blobz.git",
18
+ "directory": "packages/react"
19
+ },
20
+ "sideEffects": false,
21
+ "type": "module",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/avatar.d.ts",
25
+ "import": "./dist/avatar.js",
26
+ "default": "./dist/avatar.js"
27
+ },
28
+ "./custom": {
29
+ "types": "./dist/custom-avatar.d.ts",
30
+ "import": "./dist/custom-avatar.js",
31
+ "default": "./dist/custom-avatar.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsup src/avatar.tsx src/custom-avatar.tsx --format esm --dts --clean --sourcemap --external react --external @blobz/avatar-core",
40
+ "check-types": "tsc --noEmit",
41
+ "prepublishOnly": "bun run check-types && bun run test && bun run build",
42
+ "test": "bun test"
43
+ },
44
+ "dependencies": {
45
+ "@blobz/avatar-core": "^0.1.0"
46
+ },
47
+ "devDependencies": {
48
+ "@blobz/config": "0.0.0",
49
+ "@types/node": "^26.4.1",
50
+ "@types/react": "^19.2.18",
51
+ "@types/react-dom": "^19.2.7",
52
+ "react": "^19.2.8",
53
+ "react-dom": "^19.2.8",
54
+ "typescript": "^6.0.3"
55
+ },
56
+ "peerDependencies": {
57
+ "react": ">=19.0.0"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public",
61
+ "registry": "https://registry.npmjs.org/"
62
+ }
63
+ }