chore: Add new WDS statbox (#30744)
Fixes #30423 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced the `StatBoxComponent` for displaying statistical information with flexibility in presentation, including icons, labels, values, and sublabels. - Expanded the application's widget offerings by adding the `WDSStatBoxWidget`. - **Enhancements** - Improved flexibility and customization of the Flex component with the introduction of the `isInner` property. - Enhanced feature flag checks for quicker enablement of specific functionalities. - **Documentation** - Added comprehensive configurations and documentation for the `WDSStatBoxWidget`, including property pane settings, default configurations, and meta information. - **Style** - Implemented new styling for the `StatBoxComponent`, defining its appearance including border, width, and background. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
67d20d9858
commit
7eda27b140
|
|
@ -80,6 +80,7 @@
|
|||
},
|
||||
"iconSize": {
|
||||
"1": "16px",
|
||||
"2": "24px"
|
||||
"2": "24px",
|
||||
"3": "32px"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export interface ActionGroupProps<T>
|
|||
>,
|
||||
InheritedActionButtonProps {
|
||||
orientation?: keyof typeof ACTION_GROUP_ORIENTATIONS;
|
||||
size?: keyof typeof SIZES;
|
||||
size?: Omit<keyof typeof SIZES, "large">;
|
||||
}
|
||||
|
||||
export interface ActionGroupItemProps<T> extends ButtonProps {
|
||||
|
|
|
|||
|
|
@ -39,14 +39,16 @@ export const Template = (args) => {
|
|||
|
||||
<Story name="Size">
|
||||
<Flex gap="spacing-2" direction="column">
|
||||
{Object.keys(SIZES).map((size) => (
|
||||
<ActionGroup size={size} key={size}>
|
||||
<Item key="option-1">Option 1</Item>
|
||||
<Item key="option-2">Option 2</Item>
|
||||
<Item key="option-3">Option 3</Item>
|
||||
<Item key="option-4">Option 4</Item>
|
||||
</ActionGroup>
|
||||
))}
|
||||
{Object.keys(SIZES)
|
||||
.filter((size) => !["large"].includes(size))
|
||||
.map((size) => (
|
||||
<ActionGroup size={size} key={size}>
|
||||
<Item key="option-1">Option 1</Item>
|
||||
<Item key="option-2">Option 2</Item>
|
||||
<Item key="option-3">Option 3</Item>
|
||||
<Item key="option-4">Option 4</Item>
|
||||
</ActionGroup>
|
||||
))}
|
||||
</Flex>
|
||||
</Story>
|
||||
|
||||
|
|
|
|||
|
|
@ -41,5 +41,5 @@ export interface ButtonProps extends HeadlessButtonProps {
|
|||
/** Size of the button
|
||||
* @default medium
|
||||
*/
|
||||
size?: keyof typeof SIZES;
|
||||
size?: Omit<keyof typeof SIZES, "large">;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,11 +82,13 @@ There are 2 sizes of the button component. Default size is `medium`.
|
|||
|
||||
<Story name="Size">
|
||||
<Flex gap="spacing-2">
|
||||
{Object.keys(SIZES).map((size) => (
|
||||
<div key={size} style={{ marginBottom: 10 }}>
|
||||
<Button size={size} icon="star" children={size} />
|
||||
</div>
|
||||
))}
|
||||
{Object.keys(SIZES)
|
||||
.filter((size) => !["large"].includes(size))
|
||||
.map((size) => (
|
||||
<div key={size} style={{ marginBottom: 10 }}>
|
||||
<Button size={size} icon="star" children={size} />
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
</Story>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { css } from "@emotion/css";
|
||||
import kebabCase from "lodash/kebabCase";
|
||||
|
||||
import type { FlexCssProps, CssVarValues } from "./types";
|
||||
import type { FlexCssProps, CssVarValues, FlexProps } from "./types";
|
||||
|
||||
export const flexCss = (props: FlexCssProps) => {
|
||||
const { isInner, ...rest } = props;
|
||||
|
||||
return css`
|
||||
${Object.keys(props).reduce(
|
||||
${Object.keys(rest).reduce(
|
||||
(styles, key) =>
|
||||
styles + flexStyles(key, props[key as keyof FlexCssProps]),
|
||||
styles + flexStyles(key, props[key as keyof FlexCssProps], { isInner }),
|
||||
"",
|
||||
)}
|
||||
`;
|
||||
|
|
@ -16,6 +18,7 @@ export const flexCss = (props: FlexCssProps) => {
|
|||
const flexStyles = (
|
||||
cssProp: string,
|
||||
value: FlexCssProps[keyof FlexCssProps],
|
||||
extraProps?: Pick<FlexProps, "isInner">,
|
||||
): string => {
|
||||
if (value == null) return "";
|
||||
|
||||
|
|
@ -71,6 +74,7 @@ const flexStyles = (
|
|||
kebabCase(cssProp),
|
||||
value as CssVarValues,
|
||||
cssVarValue,
|
||||
extraProps,
|
||||
)};
|
||||
`;
|
||||
default:
|
||||
|
|
@ -83,7 +87,8 @@ const flexStyles = (
|
|||
export const containerDimensionStyles = <T = FlexCssProps[keyof FlexCssProps]>(
|
||||
cssProp: string,
|
||||
value: T,
|
||||
callback?: (value: T) => void,
|
||||
callback?: (value: T, extraProps?: Pick<FlexProps, "isInner">) => void,
|
||||
extraProps?: Pick<FlexProps, "isInner">,
|
||||
) => {
|
||||
if (value == null) return;
|
||||
|
||||
|
|
@ -95,20 +100,22 @@ export const containerDimensionStyles = <T = FlexCssProps[keyof FlexCssProps]>(
|
|||
`@container (min-width: ${current}) {& {
|
||||
${cssProp}: ${
|
||||
//@ts-expect-error: type mismatch
|
||||
callback ? callback(value[current]) : value[current]
|
||||
callback ? callback(value[current], extraProps) : value[current]
|
||||
};}}`
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
prev +
|
||||
//@ts-expect-error: type mismatch
|
||||
`${cssProp}: ${callback ? callback(value[current]) : value[current]};`
|
||||
`${cssProp}: ${
|
||||
//@ts-expect-error: type mismatch
|
||||
callback ? callback(value[current], extraProps) : value[current]
|
||||
};`
|
||||
);
|
||||
}
|
||||
}, "");
|
||||
}
|
||||
|
||||
return `${cssProp}: ${callback ? callback(value) : value};`;
|
||||
return `${cssProp}: ${callback ? callback(value, extraProps) : value};`;
|
||||
};
|
||||
|
||||
const alignItemsValue = (value: FlexCssProps["alignItems"]) => {
|
||||
|
|
@ -131,17 +138,26 @@ export const flexWrapValue = (value: FlexCssProps["wrap"]) => {
|
|||
return value;
|
||||
};
|
||||
|
||||
const cssVarValue = (value: CssVarValues) => {
|
||||
const cssVarValue = (
|
||||
value: CssVarValues,
|
||||
extraProps?: Pick<FlexProps, "isInner">,
|
||||
) => {
|
||||
const isInner = Boolean(extraProps?.isInner);
|
||||
|
||||
if (value == null) return;
|
||||
|
||||
if ((value as string).includes("sizing")) {
|
||||
return `var(--${value})`;
|
||||
}
|
||||
|
||||
if ((value as string).includes("spacing")) {
|
||||
if ((value as string).includes("spacing") && !isInner) {
|
||||
return `var(--outer-${value})`;
|
||||
}
|
||||
|
||||
if ((value as string).includes("spacing") && isInner) {
|
||||
return `var(--inner-${value})`;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -209,6 +209,8 @@ export interface FlexProps
|
|||
style?: CSSProperties;
|
||||
/** Sets the HTML [id](https://developer.mozilla.org/en-US/docs/Web/API/Element/id) for the element. */
|
||||
id?: string;
|
||||
/** used to specify what kind of spacing the component will use ( inner-spacing or outer-spacing) */
|
||||
isInner?: boolean;
|
||||
|
||||
/*
|
||||
* Events props
|
||||
|
|
|
|||
|
|
@ -6,4 +6,8 @@
|
|||
&[data-size="medium"] {
|
||||
inline-size: var(--icon-size-2);
|
||||
}
|
||||
|
||||
&[data-size="large"] {
|
||||
inline-size: var(--icon-size-3);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import type { SIZES } from "../../../shared";
|
|||
export type IconProps = Omit<HeadlessIconProps, "children"> & {
|
||||
/** Size of the icon
|
||||
* @default medium
|
||||
*
|
||||
* Note: we need large size for the icon only
|
||||
*/
|
||||
size?: keyof typeof SIZES;
|
||||
/** custom icon component
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type {
|
|||
PopoverProps,
|
||||
} from "@design-system/headless";
|
||||
import type { ReactNode } from "react";
|
||||
import type { SIZES } from "../../../shared";
|
||||
|
||||
export interface ModalProps
|
||||
extends Pick<
|
||||
|
|
@ -13,7 +14,7 @@ export interface ModalProps
|
|||
/** Size of the Modal
|
||||
* @default medium
|
||||
*/
|
||||
size?: "small" | "medium" | "large";
|
||||
size?: keyof typeof SIZES;
|
||||
/** The children of the component. */
|
||||
children: ReactNode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export interface TextInputProps extends HeadlessTextInputProps {
|
|||
*
|
||||
* @default medium
|
||||
*/
|
||||
size?: keyof typeof SIZES;
|
||||
size?: Omit<keyof typeof SIZES, "large">;
|
||||
}
|
||||
|
||||
const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
|
||||
|
|
|
|||
|
|
@ -51,14 +51,16 @@ TextInput is a component that allows users to input text.
|
|||
|
||||
<Story name="Size">
|
||||
<Flex direction="column" gap="spacing-2">
|
||||
{Object.keys(SIZES).map((size) => (
|
||||
<TextInput
|
||||
placeholder={size}
|
||||
size={size}
|
||||
startIcon={<Icon name="user" />}
|
||||
key={size}
|
||||
/>
|
||||
))}
|
||||
{Object.keys(SIZES)
|
||||
.filter((size) => !["large"].includes(size))
|
||||
.map((size) => (
|
||||
<TextInput
|
||||
placeholder={size}
|
||||
size={size}
|
||||
startIcon={<Icon name="user" />}
|
||||
key={size}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</Story>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export const SIZES = {
|
||||
small: "small",
|
||||
medium: "medium",
|
||||
large: "large",
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ import { ZoneWidget } from "./anvil/ZoneWidget";
|
|||
import { WDSHeadingWidget } from "./wds/WDSHeadingWidget";
|
||||
import { WDSParagraphWidget } from "./wds/WDSParagraphWidget";
|
||||
import { WDSModalWidget } from "./wds/WDSModalWidget";
|
||||
import { WDSStatBoxWidget } from "./wds/WDSStatBoxWidget";
|
||||
import { WDSKeyValueWidget } from "./wds/WDSKeyValueWidget";
|
||||
|
||||
const LegacyWidgets = [
|
||||
|
|
@ -168,6 +169,7 @@ const WDSWidgets = [
|
|||
WDSParagraphWidget,
|
||||
WDSHeadingWidget,
|
||||
WDSModalWidget,
|
||||
WDSStatBoxWidget,
|
||||
WDSKeyValueWidget,
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
import React from "react";
|
||||
import type { StatBoxComponentProps } from "./types";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
import { Flex, Icon, Text } from "@design-system/widgets";
|
||||
|
||||
export const StatBoxComponent = (props: StatBoxComponentProps) => {
|
||||
const {
|
||||
iconAlign,
|
||||
iconName,
|
||||
label,
|
||||
sublabel,
|
||||
value,
|
||||
valueChange,
|
||||
valueImpact,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
className={styles.statbox}
|
||||
direction={iconAlign === "end" ? "row-reverse" : "row"}
|
||||
gap="spacing-2"
|
||||
isInner
|
||||
padding="spacing-3 "
|
||||
>
|
||||
{iconName && iconName !== "(none)" && (
|
||||
<Icon name={iconName} size="large" />
|
||||
)}
|
||||
<Flex direction="column" flexGrow={1} gap="spacing-3" isInner>
|
||||
{label && (
|
||||
<Text color="neutral" lineClamp={1} variant="footnote">
|
||||
{label}
|
||||
</Text>
|
||||
)}
|
||||
{value && (
|
||||
<Flex
|
||||
alignItems="end"
|
||||
flexShrink={0}
|
||||
gap="spacing-1"
|
||||
isInner
|
||||
maxWidth="calc(100% - var(--sizing-1))"
|
||||
>
|
||||
<Text fontWeight={500} lineClamp={1} variant="subtitle">
|
||||
{value}
|
||||
</Text>
|
||||
{valueChange && (
|
||||
<Text color={valueImpact} lineClamp={1} variant="footnote">
|
||||
{valueChange}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
{sublabel && (
|
||||
<Text color="neutral" lineClamp={1} variant="footnote">
|
||||
{sublabel}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.statbox {
|
||||
border: 1px solid var(--color-bd);
|
||||
border-radius: var(--border-radius-1);
|
||||
width: 100%;
|
||||
background: var(--color-bg-elevation-2);
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import type { StatBoxWidgetProps } from "../widget/types";
|
||||
|
||||
export interface StatBoxComponentProps extends StatBoxWidgetProps {}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import type { AnvilConfig } from "WidgetProvider/constants";
|
||||
|
||||
export const anvilConfig: AnvilConfig = {
|
||||
isLargeWidget: true,
|
||||
widgetSize: {
|
||||
minWidth: "100%",
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils";
|
||||
|
||||
export const autocompleteConfig = {
|
||||
"!doc": "Show and highlight stats from your data sources",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/stat-box",
|
||||
isVisible: DefaultAutocompleteDefinitions.isVisible,
|
||||
};
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import type { WidgetDefaultProps } from "WidgetProvider/constants";
|
||||
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
|
||||
|
||||
export const defaultsConfig = {
|
||||
isVisible: true,
|
||||
widgetName: "StatBoxWidget",
|
||||
version: 1,
|
||||
animateLoading: true,
|
||||
valueImpact: "positive",
|
||||
valueChange: "+120%",
|
||||
value: "1500",
|
||||
label: "Active Users",
|
||||
sublabel: "Since 21 Jan 2022",
|
||||
icon: "user",
|
||||
responsiveBehavior: ResponsiveBehavior.Fill,
|
||||
} as unknown as WidgetDefaultProps;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export * from "./propertyPaneConfig";
|
||||
export { autocompleteConfig } from "./autocompleteConfig";
|
||||
export { defaultsConfig } from "./defaultsConfig";
|
||||
export { metaConfig } from "./metaConfig";
|
||||
export { settersConfig } from "./settersConfig";
|
||||
export { anvilConfig } from "./anvilConfig";
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import IconSVG from "../icon.svg";
|
||||
import { WIDGET_TAGS } from "constants/WidgetConstants";
|
||||
|
||||
export const metaConfig = {
|
||||
name: "Statbox",
|
||||
iconSVG: IconSVG,
|
||||
needsMeta: false,
|
||||
isCanvas: false,
|
||||
searchTags: ["statbox"],
|
||||
tags: [WIDGET_TAGS.DISPLAY],
|
||||
};
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
import { COLORS } from "@design-system/widgets";
|
||||
import { ValidationTypes } from "constants/WidgetValidation";
|
||||
import capitalize from "lodash/capitalize";
|
||||
|
||||
export const propertyPaneContentConfig = [
|
||||
{
|
||||
sectionName: "Fields",
|
||||
children: [
|
||||
{
|
||||
propertyName: "label",
|
||||
label: "Label",
|
||||
helpText: "Sets the label of the statbox",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Active users",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
{
|
||||
propertyName: "value",
|
||||
label: "Value",
|
||||
helpText: "Sets the value of the statbox",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "257",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Optional Fields",
|
||||
children: [
|
||||
{
|
||||
propertyName: "iconName",
|
||||
label: "Select icon",
|
||||
helpText: "Sets the icon to be used for the statbox",
|
||||
controlType: "ICON_SELECT_V2",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.TEXT,
|
||||
},
|
||||
},
|
||||
{
|
||||
propertyName: "iconAlign",
|
||||
label: "Position",
|
||||
helpText: "Sets the icon alignment",
|
||||
controlType: "ICON_TABS",
|
||||
defaultValue: "start",
|
||||
fullWidth: false,
|
||||
options: [
|
||||
{
|
||||
startIcon: "skip-left-line",
|
||||
value: "start",
|
||||
},
|
||||
{
|
||||
startIcon: "skip-right-line",
|
||||
value: "end",
|
||||
},
|
||||
],
|
||||
isBindProperty: false,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.TEXT,
|
||||
params: {
|
||||
allowedValues: ["start", "end"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
propertyName: "valueChange",
|
||||
label: "Value change",
|
||||
helpText: "Secondary information about the value",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "+146%",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
{
|
||||
propertyName: "valueImpact",
|
||||
label: "Impact",
|
||||
controlType: "DROP_DOWN",
|
||||
fullWidth: true,
|
||||
helpText: "Emphasizes the change's semantic impact",
|
||||
options: Object.values(COLORS).map((semantic) => ({
|
||||
label: capitalize(semantic),
|
||||
value: semantic,
|
||||
})),
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.TEXT,
|
||||
params: {
|
||||
allowedValues: Object.values(COLORS),
|
||||
default: COLORS.accent,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
propertyName: "sublabel",
|
||||
label: "Sub label",
|
||||
helpText: "Sets the sublabel of the statbox",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Since 21 Jan 2022",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "General",
|
||||
children: [
|
||||
{
|
||||
propertyName: "isVisible",
|
||||
label: "Visible",
|
||||
helpText: "Controls the visibility of the widget",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
propertyName: "animateLoading",
|
||||
label: "Animate loading",
|
||||
controlType: "SWITCH",
|
||||
helpText: "Controls the loading of the widget",
|
||||
defaultValue: true,
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export { propertyPaneContentConfig } from "./contentConfig";
|
||||
export { propertyPaneStyleConfig } from "./styleConfig";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export const propertyPaneStyleConfig = [];
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
export const settersConfig = {
|
||||
__setters: {
|
||||
setVisibility: {
|
||||
path: "isVisible",
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
};
|
||||
3
app/client/src/widgets/wds/WDSStatBoxWidget/icon.svg
Normal file
3
app/client/src/widgets/wds/WDSStatBoxWidget/icon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 8.375C1 7.61561 1.61561 7 2.375 7H21.625C22.3844 7 23 7.61561 23 8.375V16.625C23 17.3844 22.3844 18 21.625 18H2.375C1.61561 18 1 17.3844 1 16.625V8.375ZM16.7959 10.9724C16.795 10.8822 16.83 10.7961 16.8932 10.7328C16.9563 10.6695 17.0425 10.6343 17.1326 10.635L20.0171 10.6561C20.1073 10.6567 20.1941 10.6932 20.2585 10.7574C20.3227 10.8216 20.3594 10.9083 20.3602 10.9984L20.3872 13.8829C20.3864 13.972 20.3507 14.0567 20.2877 14.1187C20.2248 14.1807 20.1395 14.2151 20.0504 14.2145C19.9613 14.2138 19.8753 14.1782 19.8112 14.1153C19.747 14.0523 19.7097 13.9671 19.7074 13.878L19.6881 11.8141L16.929 14.5788C16.8659 14.642 16.7797 14.6772 16.6896 14.6766C16.5994 14.6759 16.5126 14.6395 16.4483 14.5753C16.3839 14.511 16.3473 14.4243 16.3464 14.3342C16.3456 14.244 16.3806 14.1578 16.4438 14.0945L19.2029 11.3298L17.1389 11.3148C17.0488 11.3141 16.962 11.2776 16.8977 11.2135C16.8333 11.1493 16.7968 11.0625 16.7959 10.9724ZM4.39722 10.4079V15.25H5.29956V9.43712H4.40125L2.87048 10.5167V11.4392L4.32874 10.4079H4.39722ZM8.04096 9.80775C7.67573 10.1461 7.49311 10.5825 7.49311 11.117V11.1291H8.34711V11.117C8.34711 10.8055 8.44514 10.5543 8.64122 10.3637C8.83991 10.173 9.09909 10.0777 9.41864 10.0777C9.71943 10.0777 9.9705 10.1663 10.1719 10.3435C10.3734 10.5208 10.4741 10.7423 10.4741 11.0082C10.4741 11.223 10.4096 11.4338 10.2807 11.6406C10.1518 11.8447 9.89398 12.1482 9.50726 12.551L7.54145 14.5974V15.25H11.457V14.4403H8.79831V14.3719L10.0914 13.0626C10.5775 12.5712 10.9104 12.175 11.0904 11.8742C11.273 11.5708 11.3643 11.2646 11.3643 10.9558C11.3643 10.4778 11.183 10.0817 10.8205 9.76746C10.4579 9.45328 10.0001 9.29611 9.44683 9.29611C8.87483 9.29611 8.40623 9.46668 8.04096 9.80775Z" fill="#4C5664"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
3
app/client/src/widgets/wds/WDSStatBoxWidget/index.tsx
Normal file
3
app/client/src/widgets/wds/WDSStatBoxWidget/index.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { WDSStatBoxWidget } from "./widget";
|
||||
|
||||
export { WDSStatBoxWidget };
|
||||
59
app/client/src/widgets/wds/WDSStatBoxWidget/widget/index.tsx
Normal file
59
app/client/src/widgets/wds/WDSStatBoxWidget/widget/index.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import React from "react";
|
||||
import type { SetterConfig } from "entities/AppTheming";
|
||||
import type { WidgetState } from "widgets/BaseWidget";
|
||||
import BaseWidget from "widgets/BaseWidget";
|
||||
|
||||
import {
|
||||
metaConfig,
|
||||
defaultsConfig,
|
||||
autocompleteConfig,
|
||||
propertyPaneContentConfig,
|
||||
propertyPaneStyleConfig,
|
||||
settersConfig,
|
||||
anvilConfig,
|
||||
} from "./../config";
|
||||
import { StatBoxComponent } from "../component";
|
||||
import type { StatBoxWidgetProps } from "./types";
|
||||
import type { AnvilConfig } from "WidgetProvider/constants";
|
||||
|
||||
class WDSStatBoxWidget extends BaseWidget<StatBoxWidgetProps, WidgetState> {
|
||||
constructor(props: StatBoxWidgetProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
static type = "WDS_STATBOX_WIDGET";
|
||||
|
||||
static getConfig() {
|
||||
return metaConfig;
|
||||
}
|
||||
|
||||
static getDefaults() {
|
||||
return defaultsConfig;
|
||||
}
|
||||
|
||||
static getAutocompleteDefinitions() {
|
||||
return autocompleteConfig;
|
||||
}
|
||||
|
||||
static getPropertyPaneContentConfig() {
|
||||
return propertyPaneContentConfig;
|
||||
}
|
||||
|
||||
static getPropertyPaneStyleConfig() {
|
||||
return propertyPaneStyleConfig;
|
||||
}
|
||||
|
||||
static getSetterConfig(): SetterConfig {
|
||||
return settersConfig;
|
||||
}
|
||||
|
||||
static getAnvilConfig(): AnvilConfig | null {
|
||||
return anvilConfig;
|
||||
}
|
||||
|
||||
getWidgetView() {
|
||||
return <StatBoxComponent {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export { WDSStatBoxWidget };
|
||||
12
app/client/src/widgets/wds/WDSStatBoxWidget/widget/types.ts
Normal file
12
app/client/src/widgets/wds/WDSStatBoxWidget/widget/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import type { COLORS, IconProps } from "@design-system/widgets";
|
||||
|
||||
export interface StatBoxWidgetProps extends WidgetProps {
|
||||
label?: string;
|
||||
value?: string;
|
||||
iconName?: IconProps["name"] | "(none)";
|
||||
iconAlign?: "start" | "end";
|
||||
valueChange?: string;
|
||||
valueImpact?: keyof typeof COLORS;
|
||||
sublabel?: string;
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ export const WDS_V2_WIDGET_MAP = {
|
|||
RADIO_GROUP_WIDGET: "WDS_RADIO_GROUP_WIDGET",
|
||||
MENU_BUTTON_WIDGET: "WDS_MENU_BUTTON_WIDGET",
|
||||
MODAL_WIDGET: "WDS_MODAL_WIDGET",
|
||||
STATBOX_WIDGET: "WDS_STATBOX_WIDGET",
|
||||
KEY_VALUE_WIDGET: "WDS_KEY_VALUE_WIDGET",
|
||||
|
||||
// Anvil layout widgets
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user