Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sendbird/uikit-message-template

Package Overview
Dependencies
Maintainers
0
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sendbird/uikit-message-template - npm Package Compare versions

Comparing version 0.0.1-alpha.79 to 0.0.2

dist/core/transforms/colorTransform.d.ts

22

CHANGELOG.md

@@ -6,2 +6,24 @@ # Change Log

## [0.0.2](https://github.com/sendbird/sendbird-uikit-core-ts/compare/v0.0.1-alpha.79...v0.0.2) (2024-09-20)
### Bug Fixes
* apply view style, add fixme comments ([84eeba5](https://github.com/sendbird/sendbird-uikit-core-ts/commit/84eeba577fd8262e146c4387952c3c39d44f6cab))
* fixed carousel interface and clean up interface ([5f6343f](https://github.com/sendbird/sendbird-uikit-core-ts/commit/5f6343f45b9e93f13c678993f0a1c241ab3ed10e))
* remove only render in root constraint ([b182019](https://github.com/sendbird/sendbird-uikit-core-ts/commit/b1820195bc8d47cdc9d8cc039a633d30104a57e4))
* remove style ([95825f5](https://github.com/sendbird/sendbird-uikit-core-ts/commit/95825f5a1cc2819c299f4fa474bd5aa68b18df54))
* textButton styles ([415c567](https://github.com/sendbird/sendbird-uikit-core-ts/commit/415c567f9a4d07d20523c517abd59ac07a19c1c1))
* update carousel test data ([b7e32d4](https://github.com/sendbird/sendbird-uikit-core-ts/commit/b7e32d4225380402ed489107375377b37f97c352))
### Features
* add carousel handler to numberTransform ([2a78a93](https://github.com/sendbird/sendbird-uikit-core-ts/commit/2a78a933c3566377af484e361766cc142351f545))
* support number and color transforms by default ([f5342f6](https://github.com/sendbird/sendbird-uikit-core-ts/commit/f5342f68c10697abeaac604030c73f015e89b78d))
## [0.0.1-alpha.79](https://github.com/sendbird/sendbird-uikit-core-ts/compare/v0.0.1-alpha.78...v0.0.1-alpha.79) (2024-07-30)

@@ -8,0 +30,0 @@

@@ -14,2 +14,11 @@ import { Align, Layout, SizeSpec } from '../types/styles';

};
textButton: {
maxTextLines: number;
};
carousel: {
style: {
spacing: number;
maxChildWidth: number;
};
};
};

@@ -14,2 +14,11 @@ import { AlignValue, FlexSizeSpecValue, Layout } from '../types/styles';

},
textButton: {
maxTextLines: 1,
},
carousel: {
style: {
spacing: 10,
maxChildWidth: 240,
},
},
};

10

dist/core/messageTemplate.d.ts

@@ -8,3 +8,3 @@ import React from 'react';

interface ContainerProps extends React.PropsWithChildren<unknown> {
className: string;
className?: string;
}

@@ -20,11 +20,15 @@ interface MessageTemplateOptions<ParsedProperties> {

export interface MessageTemplateProps {
/**
* @description template items. ** IT IS NOT TRANSFORMED YET IN HERE **.
* */
templateItems: Template['body']['items'];
templateVersion?: Template['version'];
parentLayout?: Layout;
depth?: number;
isRoot?: boolean;
}
export declare const createMessageTemplate: <T>(opts: MessageTemplateOptions<T>) => {
MessageTemplate: ({ parentLayout, templateVersion, templateItems, }: MessageTemplateProps) => React.JSX.Element;
MessageTemplateBase: ({ templateItems, parentLayout, isRoot, }: MessageTemplateProps) => React.JSX.Element;
MessageTemplate: ({ templateVersion, templateItems, parentLayout, }: MessageTemplateProps) => React.JSX.Element;
MessageTemplateBase: ({ templateItems, templateVersion, parentLayout, depth, }: MessageTemplateProps) => React.JSX.Element;
};
export {};
import React from 'react';
import { SizeContextProvider } from '../context/SizeProvider';
import { ComponentType } from '../types/components';
import { CompositeComponentType } from '../types/components';
import { SUPPORTED_TEMPLATE_VERSIONS, isTemplateVersionSupported } from '../utils';

@@ -15,38 +14,37 @@ import { setTemplateItemId } from '../utils/templateItemId';

const renderer = opts.renderer || createRenderer();
const MessageTemplateBase = ({ templateItems, parentLayout = defaultProperties.box.layout, isRoot = false, }) => {
const renderItems = templateItems;
return (React.createElement(React.Fragment, null, renderItems.map((item, index, siblings) => {
const { properties } = parser.parse(item, { parentLayout, elemIdx: index, siblings });
const props = {
const MessageTemplateBase = ({ templateItems, templateVersion, parentLayout = defaultProperties.box.layout, depth = 0, }) => {
if (!isTemplateVersionSupported(templateVersion)) {
throw new Error(`Cannot parse template item due to unsupported template version: ${templateVersion}, ${SUPPORTED_TEMPLATE_VERSIONS}`);
}
return (React.createElement(React.Fragment, null, templateItems.map((rawItem, index, siblings) => {
const result = parser.parse(rawItem, { parentLayout, depth, elemIdx: index, siblings });
const item = result.transformed;
const rendererProps = {
key: index,
siblings,
parentLayout,
parsedProperties: properties,
siblings,
parsedProperties: result.properties,
};
switch (item.type) {
// CompositeComponent should be top level component
case CompositeComponentType.Carousel: {
if (!isRoot) {
throw new Error('Cannot parse template item as Carousel if the template item is not the root.');
}
if (!item.items || !Array.isArray(item.items) || item.items.length === 0) {
case ComponentType.Carousel: {
if (!Array.isArray(item.items) || item.items.length === 0) {
throw new Error('Cannot parse template item as Carousel if carousel has no items.');
}
return (React.createElement(renderer.carouselView, Object.assign({}, item, props), item.items.map((items, index) => (React.createElement(MessageTemplateBase, { key: index, templateItems: items || [], parentLayout: parentLayout })))));
return (React.createElement(renderer.carouselView, Object.assign({}, item, rendererProps), item.items.map((template, index) => (React.createElement(MessageTemplateBase, { key: index, templateItems: template.body.items || [], depth: depth + 1, templateVersion: template.version })))));
}
case ComponentType.Box: {
return (React.createElement(renderer.box, Object.assign({}, item, props),
React.createElement(MessageTemplateBase, { templateItems: item.items || [], parentLayout: item.layout })));
return (React.createElement(renderer.box, Object.assign({}, item, rendererProps),
React.createElement(MessageTemplateBase, { templateItems: item.items || [], parentLayout: item.layout, depth: depth + 1, templateVersion: templateVersion })));
}
case ComponentType.Text: {
return React.createElement(renderer.text, Object.assign({}, item, props));
return React.createElement(renderer.text, Object.assign({}, item, rendererProps));
}
case ComponentType.Image: {
return React.createElement(renderer.image, Object.assign({}, item, props));
return React.createElement(renderer.image, Object.assign({}, item, rendererProps));
}
case ComponentType.TextButton: {
return React.createElement(renderer.textButton, Object.assign({}, item, props));
return React.createElement(renderer.textButton, Object.assign({}, item, rendererProps));
}
case ComponentType.ImageButton: {
return React.createElement(renderer.imageButton, Object.assign({}, item, props));
return React.createElement(renderer.imageButton, Object.assign({}, item, rendererProps));
}

@@ -61,17 +59,7 @@ default: {

return {
MessageTemplate: ({ parentLayout = defaultProperties.rootLayout, templateVersion, templateItems, }) => {
MessageTemplate: ({ templateVersion, templateItems, parentLayout = defaultProperties.rootLayout, }) => {
const items = setTemplateItemId(templateItems);
if (!isTemplateVersionSupported(templateVersion)) {
/**
* I choose to throw instead of returning UnknownMessage because UnknownMessage syntax (required prop: item) is not what UIKit wants.
*/
throw new Error(`Cannot parse template item due to unsupported template version: ${templateVersion}, ${SUPPORTED_TEMPLATE_VERSIONS}`);
}
let isCarousel = false;
if (Array.isArray(items) && items.length > 0) {
isCarousel = items[0].type === CompositeComponentType.Carousel;
}
return (React.createElement(SizeContextProvider, null,
React.createElement(Container, { className: isCarousel ? 'sb-message-template__parent_for_carousel' : '' },
React.createElement(MessageTemplateBase, { parentLayout: parentLayout, templateItems: items, isRoot: true }))));
React.createElement(Container, null,
React.createElement(MessageTemplateBase, { isRoot: true, parentLayout: parentLayout, templateItems: items, templateVersion: templateVersion }))));
},

@@ -78,0 +66,0 @@ MessageTemplateBase,

@@ -5,6 +5,17 @@ import { Box, type Carousel, ComponentsUnion, Image, ImageButton, Text, TextButton } from '../types/components';

parentLayout: Layout;
depth?: number;
elemIdx?: number;
siblings?: Array<ComponentsUnion['properties']>;
};
export type ParserTransform = <T extends ComponentsUnion['properties'] = ComponentsUnion['properties']>(properties: T) => T;
/**
* ParserTransform is a function that is called before parsing the component properties.
* It performs preprocessing on specific component properties and then returns them.
*
* Caution: The returned property must always be a new immutable object.
* Failing to adhere to this rule can affect the original property object, potentially leading to bugs.
* For example, a transform that reverses a specific string must guarantee consistent results even if executed N times.
* */
export type ParserTransform = {
run: <T extends ComponentsUnion['properties'] = ComponentsUnion['properties']>(properties: T) => T;
};
interface CreateParserParams<ParsedProperties> {

@@ -21,5 +32,8 @@ defaultMapper?(...args: any[]): any;

export interface Parser<ParsedProperties> {
parse(properties: ComponentsUnion['properties'], options: ParserMapOptions): {
parse<T extends ComponentsUnion['properties'] = ComponentsUnion['properties']>(properties: T, options: ParserMapOptions): {
transformed: T;
properties: ParsedProperties | undefined;
};
setTransforms(transforms: ParserTransform[]): void;
addTransforms(transforms: ParserTransform[]): void;
}

@@ -26,0 +40,0 @@ export declare const createParser: <ParsedProperties = string | object>(params?: CreateParserParams<ParsedProperties> | undefined) => Parser<ParsedProperties>;

// -------- Set property mapper
import { ComponentType, CompositeComponentType, } from '../types/components';
import { ComponentType, } from '../types/components';
import { colorTransform } from './transforms/colorTransform';
import { numberTransform } from './transforms/numberTransform';
const MAPPER = () => undefined;
export const createParser = (params) => {
var _a;
const defaultMapper = (params === null || params === void 0 ? void 0 : params.defaultMapper) || MAPPER;

@@ -15,27 +18,47 @@ const mapper = {

};
const transforms = (params === null || params === void 0 ? void 0 : params.transforms) || [];
const transforms = [colorTransform, numberTransform, ...((_a = params === null || params === void 0 ? void 0 : params.transforms) !== null && _a !== void 0 ? _a : [])];
const transformDirty = new Set();
return {
setTransforms(newTransforms) {
transforms.length = 0;
transforms.push(...newTransforms);
transformDirty.clear();
},
addTransforms(newTransforms) {
transforms.push(...newTransforms);
},
parse(rawItem, options) {
const item = transforms.reduce((it, transform) => transform(it), rawItem);
// Note: it is for mutable transforms
// const uniqId =
// typeof options.depth === 'number' && typeof options.elemIdx === 'number'
// ? `id-${options.depth}-${options.elemIdx}`
// : rawItem.id ?? rawItem.elementId ?? JSON.stringify(rawItem);
//
// let item = rawItem;
// if (!transformDirty.has(uniqId)) {
// item = transforms.reduce((it, transform) => transform(it), rawItem);
// transformDirty.add(uniqId);
// }
const item = transforms.reduce((it, transform) => transform.run(it), rawItem);
switch (item.type) {
case ComponentType.Box: {
return { properties: mapper.mapBoxProps(item, options) };
return { transformed: item, properties: mapper.mapBoxProps(item, options) };
}
case ComponentType.Text: {
return { properties: mapper.mapTextProps(item, options) };
return { transformed: item, properties: mapper.mapTextProps(item, options) };
}
case ComponentType.Image: {
return { properties: mapper.mapImageProps(item, options) };
return { transformed: item, properties: mapper.mapImageProps(item, options) };
}
case ComponentType.TextButton: {
return { properties: mapper.mapTextButtonProps(item, options) };
return { transformed: item, properties: mapper.mapTextButtonProps(item, options) };
}
case ComponentType.ImageButton: {
return { properties: mapper.mapImageButtonProps(item, options) };
return { transformed: item, properties: mapper.mapImageButtonProps(item, options) };
}
case CompositeComponentType.Carousel: {
return { properties: mapper.mapCarouselProps(item, options) };
case ComponentType.Carousel: {
return { transformed: item, properties: mapper.mapCarouselProps(item, options) };
}
default:
return { properties: undefined };
return { transformed: item, properties: undefined };
}

@@ -42,0 +65,0 @@ },

import React from 'react';
import type { BasicProps, ComponentType, ComponentsUnion, CompositeComponentType, GetProperties } from '../types/components';
import type { BasicProps, ComponentType, ComponentsUnion, GetProperties } from '../types/components';
type FuncComponent<Props, ParsedStyle> = (props: BasicProps<Props, ParsedStyle>) => React.ReactElement | null;
type ComponentProperties<Type extends ComponentType | CompositeComponentType> = GetProperties<Type, ComponentsUnion>;
type ComponentProperties<Type extends ComponentType> = GetProperties<Type, ComponentsUnion>;
interface CreateRendererParams<ParsedStyle> {
views?: {
[component in ComponentType | CompositeComponentType]?: FuncComponent<ComponentProperties<component>, ParsedStyle>;
[component in ComponentType]?: FuncComponent<ComponentProperties<component>, ParsedStyle>;
};
}
export type Renderer<ParsedStyle> = {
[component in ComponentType | CompositeComponentType]: FuncComponent<ComponentProperties<component>, ParsedStyle>;
[component in ComponentType]: FuncComponent<ComponentProperties<component>, ParsedStyle>;
};
export declare function createRenderer<ParsedStyle = object | string>(params?: CreateRendererParams<ParsedStyle>): Renderer<ParsedStyle>;
export {};
import type React from 'react';
import type { Action } from './properties';
import type { Align, ImageMetaData, ImageStyle, Layout, SizeSpec, TextStyle, ViewStyle } from './styles';
import type { Align, CarouselStyle, ImageMetaData, ImageStyle, Layout, SizeSpec, TextStyle, ViewStyle } from './styles';
import type { Template } from './template';
export type BasicProps<T, ParsedProperties> = React.PropsWithChildren<T & {

@@ -10,3 +11,3 @@ key: number;

}>;
export type GetProperties<Type extends ComponentType | CompositeComponentType, U extends ComponentsUnion> = U extends {
export type GetProperties<Type extends ComponentType, U extends ComponentsUnion> = U extends {
type: Type;

@@ -31,3 +32,3 @@ properties: infer P;

} | {
type: CompositeComponentType.Carousel;
type: ComponentType.Carousel;
properties: Carousel;

@@ -40,5 +41,3 @@ };

TextButton = "textButton",
ImageButton = "imageButton"
}
export declare enum CompositeComponentType {
ImageButton = "imageButton",
Carousel = "carouselView"

@@ -54,3 +53,3 @@ }

id?: string;
type: ComponentType | CompositeComponentType;
type: ComponentType;
action?: Action;

@@ -94,5 +93,5 @@ width?: SizeSpec;

export interface Carousel extends View {
type: CompositeComponentType.Carousel;
items: Array<ComponentsUnion['properties'][]>;
spacing?: number;
type: ComponentType.Carousel;
items: Template[];
carouselStyle?: CarouselStyle;
}

@@ -8,6 +8,3 @@ export var ComponentType;

ComponentType["ImageButton"] = "imageButton";
ComponentType["Carousel"] = "carouselView";
})(ComponentType || (ComponentType = {}));
export var CompositeComponentType;
(function (CompositeComponentType) {
CompositeComponentType["Carousel"] = "carouselView";
})(CompositeComponentType || (CompositeComponentType = {}));

@@ -75,1 +75,5 @@ export declare enum Layout {

}
export interface CarouselStyle {
spacing?: number;
maxChildWidth?: number;
}
import { AlignValue } from '../types/styles';
export declare const SUPPORTED_TEMPLATE_VERSIONS: number[];
export declare const alignInFlex: (align: AlignValue) => "flex-end" | "center" | "flex-start";
export declare const isTemplateVersionSupported: (templateVersion?: number) => boolean;
export declare const isTemplateVersionSupported: (templateVersion?: number | string) => boolean;

@@ -22,3 +22,3 @@ import { AlignValue } from '../types/styles';

return true;
return SUPPORTED_TEMPLATE_VERSIONS.includes(templateVersion);
return SUPPORTED_TEMPLATE_VERSIONS.includes(Number(templateVersion));
};
{
"name": "@sendbird/uikit-message-template",
"version": "0.0.1-alpha.79",
"version": "0.0.2",
"publishConfig": {

@@ -35,3 +35,3 @@ "registry": "https://registry.npmjs.org/",

},
"gitHead": "4c4f0cc4e8dc3612a0eb7b28b67652969d04f0c5"
"gitHead": "dcd044768de8cca0a69e7bbb6a33c95ce6223972"
}

@@ -15,2 +15,11 @@ import { Align, AlignValue, FlexSizeSpecValue, Layout, SizeSpec } from '../types/styles';

},
textButton: {
maxTextLines: 1,
},
carousel: {
style: {
spacing: 10,
maxChildWidth: 240,
},
},
};

@@ -7,3 +7,2 @@ // -------- Set property mapper

ComponentsUnion,
CompositeComponentType,
Image,

@@ -15,5 +14,8 @@ ImageButton,

import type { Layout } from '../types/styles';
import { colorTransform } from './transforms/colorTransform';
import { numberTransform } from './transforms/numberTransform';
export type ParserMapOptions = {
parentLayout: Layout;
depth?: number;
elemIdx?: number;

@@ -23,5 +25,13 @@ siblings?: Array<ComponentsUnion['properties']>;

export type ParserTransform = <T extends ComponentsUnion['properties'] = ComponentsUnion['properties']>(
properties: T,
) => T;
/**
* ParserTransform is a function that is called before parsing the component properties.
* It performs preprocessing on specific component properties and then returns them.
*
* Caution: The returned property must always be a new immutable object.
* Failing to adhere to this rule can affect the original property object, potentially leading to bugs.
* For example, a transform that reverses a specific string must guarantee consistent results even if executed N times.
* */
export type ParserTransform = {
run: <T extends ComponentsUnion['properties'] = ComponentsUnion['properties']>(properties: T) => T;
};

@@ -40,8 +50,11 @@ interface CreateParserParams<ParsedProperties> {

export interface Parser<ParsedProperties> {
parse(
properties: ComponentsUnion['properties'],
parse<T extends ComponentsUnion['properties'] = ComponentsUnion['properties']>(
properties: T,
options: ParserMapOptions,
): {
transformed: T;
properties: ParsedProperties | undefined;
};
setTransforms(transforms: ParserTransform[]): void;
addTransforms(transforms: ParserTransform[]): void;
}

@@ -64,28 +77,51 @@

};
const transforms = params?.transforms || [];
const transforms = [colorTransform, numberTransform, ...(params?.transforms ?? [])];
const transformDirty = new Set<string>();
return {
setTransforms(newTransforms: ParserTransform[]) {
transforms.length = 0;
transforms.push(...newTransforms);
transformDirty.clear();
},
addTransforms(newTransforms: ParserTransform[]) {
transforms.push(...newTransforms);
},
parse(rawItem, options) {
const item = transforms.reduce((it, transform) => transform(it), rawItem);
// Note: it is for mutable transforms
// const uniqId =
// typeof options.depth === 'number' && typeof options.elemIdx === 'number'
// ? `id-${options.depth}-${options.elemIdx}`
// : rawItem.id ?? rawItem.elementId ?? JSON.stringify(rawItem);
//
// let item = rawItem;
// if (!transformDirty.has(uniqId)) {
// item = transforms.reduce((it, transform) => transform(it), rawItem);
// transformDirty.add(uniqId);
// }
const item = transforms.reduce((it, transform) => transform.run(it), rawItem);
switch (item.type) {
case ComponentType.Box: {
return { properties: mapper.mapBoxProps(item, options) };
return { transformed: item, properties: mapper.mapBoxProps(item, options) };
}
case ComponentType.Text: {
return { properties: mapper.mapTextProps(item, options) };
return { transformed: item, properties: mapper.mapTextProps(item, options) };
}
case ComponentType.Image: {
return { properties: mapper.mapImageProps(item, options) };
return { transformed: item, properties: mapper.mapImageProps(item, options) };
}
case ComponentType.TextButton: {
return { properties: mapper.mapTextButtonProps(item, options) };
return { transformed: item, properties: mapper.mapTextButtonProps(item, options) };
}
case ComponentType.ImageButton: {
return { properties: mapper.mapImageButtonProps(item, options) };
return { transformed: item, properties: mapper.mapImageButtonProps(item, options) };
}
case CompositeComponentType.Carousel: {
return { properties: mapper.mapCarouselProps(item, options) };
case ComponentType.Carousel: {
return { transformed: item, properties: mapper.mapCarouselProps(item, options) };
}
default:
return { properties: undefined };
return { transformed: item, properties: undefined };
}

@@ -92,0 +128,0 @@ },

import type React from 'react';
import type { Action } from './properties';
import type { Align, ImageMetaData, ImageStyle, Layout, SizeSpec, TextStyle, ViewStyle } from './styles';
import type { Align, CarouselStyle, ImageMetaData, ImageStyle, Layout, SizeSpec, TextStyle, ViewStyle } from './styles';
import type { Template } from './template';

@@ -15,3 +16,3 @@ export type BasicProps<T, ParsedProperties> = React.PropsWithChildren<

export type GetProperties<Type extends ComponentType | CompositeComponentType, U extends ComponentsUnion> = U extends {
export type GetProperties<Type extends ComponentType, U extends ComponentsUnion> = U extends {
type: Type;

@@ -45,3 +46,3 @@ properties: infer P;

| {
type: CompositeComponentType.Carousel;
type: ComponentType.Carousel;
properties: Carousel;

@@ -56,5 +57,2 @@ };

ImageButton = 'imageButton',
}
export enum CompositeComponentType {
Carousel = 'carouselView',

@@ -71,3 +69,3 @@ }

id?: string;
type: ComponentType | CompositeComponentType;
type: ComponentType;
action?: Action;

@@ -117,5 +115,5 @@ width?: SizeSpec;

export interface Carousel extends View {
type: CompositeComponentType.Carousel;
items: Array<ComponentsUnion['properties'][]>;
spacing?: number;
type: ComponentType.Carousel;
items: Template[];
carouselStyle?: CarouselStyle;
}

@@ -92,1 +92,6 @@ export enum Layout {

}
export interface CarouselStyle {
spacing?: number;
maxChildWidth?: number;
}

@@ -24,5 +24,5 @@ import { AlignValue } from '../types/styles';

export const isTemplateVersionSupported = (templateVersion?: number) => {
export const isTemplateVersionSupported = (templateVersion?: number | string) => {
if (!templateVersion) return true;
return SUPPORTED_TEMPLATE_VERSIONS.includes(templateVersion);
return SUPPORTED_TEMPLATE_VERSIONS.includes(Number(templateVersion));
};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc