icon.tsx 4.1 KB

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