1import React, { CSSProperties } from 'react';
2
3import { CanvasElementItem, CanvasElementProps } from '../element';
4import {
5  ColorDimensionConfig,
6  ResourceDimensionConfig,
7  ResourceDimensionMode,
8  getPublicOrAbsoluteUrl,
9} from 'app/features/dimensions';
10import { ColorDimensionEditor, ResourceDimensionEditor } from 'app/features/dimensions/editors';
11import SVG from 'react-inlinesvg';
12import { css } from '@emotion/css';
13import { isString } from 'lodash';
14import { LineConfig } from '../types';
15import { DimensionContext } from 'app/features/dimensions/context';
16
17export interface IconConfig {
18  path?: ResourceDimensionConfig;
19  fill?: ColorDimensionConfig;
20  stroke?: LineConfig;
21}
22
23interface IconData {
24  path: string;
25  fill: string;
26  strokeColor?: string;
27  stroke?: number;
28}
29
30// When a stoke is defined, we want the path to be in page units
31const svgStrokePathClass = css`
32  path {
33    vector-effect: non-scaling-stroke;
34  }
35`;
36
37export function IconDisplay(props: CanvasElementProps) {
38  const { width, height, data } = props;
39  if (!data?.path) {
40    return null;
41  }
42
43  const svgStyle: CSSProperties = {
44    fill: data?.fill,
45    stroke: data?.strokeColor,
46    strokeWidth: data?.stroke,
47  };
48
49  return (
50    <SVG
51      src={data.path}
52      width={width}
53      height={height}
54      style={svgStyle}
55      className={svgStyle.strokeWidth ? svgStrokePathClass : undefined}
56    />
57  );
58}
59
60export const iconItem: CanvasElementItem<IconConfig, IconData> = {
61  id: 'icon',
62  name: 'Icon',
63  description: 'SVG Icon display',
64
65  display: IconDisplay,
66
67  getNewOptions: (options) => ({
68    placement: {
69      width: 50,
70      height: 50,
71    },
72    ...options,
73    config: {
74      path: {
75        mode: ResourceDimensionMode.Fixed,
76        fixed: 'img/icons/unicons/question-circle.svg',
77      },
78      fill: { fixed: '#FFF899' },
79    },
80  }),
81
82  // Called when data changes
83  prepareData: (ctx: DimensionContext, cfg: IconConfig) => {
84    let path: string | undefined = undefined;
85    if (cfg.path) {
86      path = ctx.getResource(cfg.path).value();
87    }
88    if (!path || !isString(path)) {
89      path = getPublicOrAbsoluteUrl('img/icons/unicons/question-circle.svg');
90    }
91
92    const data: IconData = {
93      path,
94      fill: cfg.fill ? ctx.getColor(cfg.fill).value() : '#CCC',
95    };
96
97    if (cfg.stroke?.width && cfg.stroke.color) {
98      if (cfg.stroke.width > 0) {
99        data.stroke = cfg.stroke?.width;
100        data.strokeColor = ctx.getColor(cfg.stroke.color).value();
101      }
102    }
103    return data;
104  },
105
106  // Heatmap overlay options
107  registerOptionsUI: (builder) => {
108    const category = ['Icon'];
109    builder
110      .addCustomEditor({
111        category,
112        id: 'iconSelector',
113        path: 'config.path',
114        name: 'SVG Path',
115        editor: ResourceDimensionEditor,
116        settings: {
117          resourceType: 'icon',
118        },
119      })
120      .addCustomEditor({
121        category,
122        id: 'config.fill',
123        path: 'config.fill',
124        name: 'Fill color',
125        editor: ColorDimensionEditor,
126        settings: {},
127        defaultValue: {
128          // Configured values
129          fixed: 'grey',
130        },
131      })
132      .addSliderInput({
133        category,
134        path: 'config.stroke.width',
135        name: 'Stroke',
136        defaultValue: 0,
137        settings: {
138          min: 0,
139          max: 10,
140        },
141      })
142      .addCustomEditor({
143        category,
144        id: 'config.stroke.color',
145        path: 'config.stroke.color',
146        name: 'Stroke color',
147        editor: ColorDimensionEditor,
148        settings: {},
149        defaultValue: {
150          // Configured values
151          fixed: 'grey',
152        },
153        showIf: (cfg) => Boolean(cfg?.config?.stroke?.width),
154      });
155  },
156};
157