From ac2dbafc64e843ab6f7d0d8582b4fa2e365ae6d5 Mon Sep 17 00:00:00 2001 From: shafeeqz <58530222+shafeeqz@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:59:40 +0200 Subject: [PATCH] Adding OnObject Formatting (#125) * Add on object formatting support * Update Bar chart to use formattingId * Update barChartSettingsModel.ts * Update capabilities.json * Update package.json * use onobjectutils package * comments * lint fixes and comments * iupdate tutorials * update onobject tutorial --------- Co-authored-by: Shafeeq --- Tutorial/ColorPalette.md | 52 +- Tutorial/ConditionalFormatting.md | 19 +- Tutorial/DataBinding.md | 13 +- Tutorial/DataBoundObjects.md | 102 +- Tutorial/ExtensibilityUtils.md | 4 +- Tutorial/ExternalLibraries.md | 2 +- Tutorial/HighContrastSupport.md | 14 +- Tutorial/LaunchURL.md | 20 +- Tutorial/Locale.md | 9 +- Tutorial/OnObject.md | 364 +++++++ Tutorial/ReportPageTooltips.md | 7 +- Tutorial/Selection.md | 91 +- Tutorial/StaticObjects.md | 254 +++-- Tutorial/Typings.md | 3 +- capabilities.json | 96 +- package-lock.json | 1600 ++++++++++++----------------- package.json | 10 +- pbiviz.json | 4 +- src/barChart.ts | 677 ++++++++---- src/barChartSettingsModel.ts | 192 ++++ style/visual.less | 22 +- 21 files changed, 2154 insertions(+), 1401 deletions(-) create mode 100644 Tutorial/OnObject.md create mode 100644 src/barChartSettingsModel.ts diff --git a/Tutorial/ColorPalette.md b/Tutorial/ColorPalette.md index bdb8bd3..16c1589 100644 --- a/Tutorial/ColorPalette.md +++ b/Tutorial/ColorPalette.md @@ -26,16 +26,48 @@ interface BarChartDataPoint { `colorPalette` is a service that manages the colors used on your visual. An instance of it is available on `IVisualHost`. ## Assigning Color to Data Points -We defined `visualTransform` as a construct to convert `dataView` to a view model Bar Chart can use. -Since we iterate through the data points in `visualTransform` it is also the ideal place to assign colors. +We defined `createSelectorDataPoints` as a construct to convert options `dataView` to Bar Chart data points that will be used in visual view. +Since we iterate through the data points in `createSelectorDataPoints` it is also the ideal place to assign colors. ```typescript -let colorPalette: IColorPalette = host.colorPalette; // host: IVisualHost -for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { - barChartDataPoints.push({ - category: category.values[i], - value: dataValue.values[i], - color: colorPalette.getColor(category.values[i]).value, - }); + +function createSelectorDataPoints(options: VisualUpdateOptions, host: IVisualHost): BarChartDataPoint[] { + let barChartDataPoints: BarChartDataPoint[] = [] + const dataViews = options.dataViews; + if (!dataViews + || !dataViews[0] + || !dataViews[0].categorical + || !dataViews[0].categorical.categories + || !dataViews[0].categorical.categories[0].source + || !dataViews[0].categorical.values + ) { + return barChartDataPoints; + } + + const categorical = dataViews[0].categorical; + const category = categorical.categories[0]; + const dataValue = categorical.values[0]; + + const colorPalette: ISandboxExtendedColorPalette = host.colorPalette; + const strokeColor: string = getColumnStrokeColor(colorPalette); + const strokeWidth: number = getColumnStrokeWidth(colorPalette.isHighContrast); + + for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { + const color: string = getColumnColorByIndex(category, i, colorPalette); + + const selectionId: ISelectionId = host.createSelectionIdBuilder() + .withCategory(category, i) + .createSelectionId(); + + barChartDataPoints.push({ + color, + strokeColor, + strokeWidth, + selectionId, + value: dataValue.values[i], + category: `${category.values[i]}`, + }); + } + return barChartDataPoints; } -``` +``` \ No newline at end of file diff --git a/Tutorial/ConditionalFormatting.md b/Tutorial/ConditionalFormatting.md index 2e87772..dde89b0 100644 --- a/Tutorial/ConditionalFormatting.md +++ b/Tutorial/ConditionalFormatting.md @@ -1,7 +1,6 @@ # Adding conditional formatting to your Visual -[Conditional formatting](https://docs.microsoft.com/en-us/power-bi/visuals/service-tips-and-tricks-for-color-formatting#conditional-formatting-for-visualizations) of custom properties is supported by updating `VisualObjectInstance` object's properties as enumerated under `enumerateObjectInstances` method. - -See [commit](https://github.com/microsoft/powerbi-visuals-api/commit/8fe88399c5ba82feeec4541ce5bf8e02a3ecd15a) for what was added at this step. +[Conditional formatting](https://docs.microsoft.com/en-us/power-bi/visuals/service-tips-and-tricks-for-color-formatting#conditional-formatting-for-visualizations) of custom formatting properties is supported by setting formatting property `instanceKind` in `getFormattingModel` method. +For more info on conditional formatting click [here](https://learn.microsoft.com/en-us/power-bi/developer/visuals/conditional-format?tabs=getFormattingModel) Conditional formatting can only be applied to the following property types: * Color @@ -10,25 +9,23 @@ Conditional formatting can only be applied to the following property types: * Web URL ## Add a conditional color formatting entry in the format pane -To add the conditional color formatting button in the format pane for the desired object, under the `enumerateObjectInstances` method, make the following change: +To add the conditional color formatting button in the format pane for the desired object, under the `getFormattingModel` method, make the following change: -Via `propertyInstanceKind` property of enumerated `VisualObjectInstance`, list all the properties that you'd like to have the conditional formatting entry applied to in the format pane. +Define `instanceKind` property of required formatting property `descriptor` with the appropriate value. Use `VisualEnumerationInstanceKinds` enum to declare the type of the desired format (constant, rule or both). ```typescript // List your conditional formatting properties -propertyInstanceKind: { - fill: VisualEnumerationInstanceKinds.ConstantOrRule -} +instanceKind: powerbi.VisualEnumerationInstanceKinds.ConstantOrRule ``` ![](images/ConditionalFormattingEntry.png) ## Define how conditional formatting behaves Using `createDataViewWildcardSelector` declared under `powerbi-visuals-utils-dataviewutils`, specify whether conditional formatting will be applied to instances, totals, or both. For more information, see [DataViewWildcard](https://docs.microsoft.com/en-us/power-bi/developer/visuals/utils-dataview#dataviewwildcard). -In `enumerateObjectInstances`, make the following changes to the objects you want to apply conditional formatting to: +In `BarChartFormattingSettingsModel`, make the following changes to the formatting properties you want to apply conditional formatting to: -* Replace the `VisualObjectInstance`'s `selector` value with a `dataViewWildcard.createDataViewWildcardSelector()` call. Specify the desired option from `DataViewWildcardMatchingOption` enum to define whether conditional formatting is applied to instances, totals, or both. +* Replace the formatting property `descriptor`'s `selector` value with a `dataViewWildcard.createDataViewWildcardSelector()` call. Specify the desired option from `DataViewWildcardMatchingOption` enum to define whether conditional formatting is applied to instances, totals, or both. * Add the `altConstantValueSelector` property having the value previously defined for the `selector` property. @@ -39,6 +36,6 @@ selector: dataViewWildcard.createDataViewWildcardSelector(dataViewWildcard.DataV // Add this property with the value previously defined for the selector property altConstantValueSelector: barDataPoint.selectionId.getSelector() ``` -See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/commit/956923b641bb1eacb613bf55a91f77725bc42431) for how conditional formatting was applied to sample bar chart. +See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart) for how conditional formatting was applied to sample bar chart. ![](images/CondFormatSupport.png) \ No newline at end of file diff --git a/Tutorial/DataBinding.md b/Tutorial/DataBinding.md index 79fad8f..569fc64 100644 --- a/Tutorial/DataBinding.md +++ b/Tutorial/DataBinding.md @@ -57,16 +57,16 @@ Use the internal `name` defined in your dataRoles to reference each field. For more information, see the section about [Data View Mapping](https://github.com/Microsoft/PowerBI-visuals/blob/master/Capabilities/DataViewMappings.md). -## Defining and Using `visualTransform` +## Defining and Using `createSelectorDataPoints` DataView is the structure that PowerBI provides to your visual and it contains the queried data to be visualized. However, DataView provides your data in different forms such as categorical and table forms. In this instance we're building a categorical visual and we will only need the use the categorical property on the DataView. -Defining visualTransform will allow you to convert DataView into a view model your visual will use. +Defining `createSelectorDataPoints` will allow you to convert options dataView into bar chart data points your visual will use. IVisualHost is required because when defining individual data points, you will want to assign colors and selection to them. ```typescript /** - * Function that converts queried data into a view model that will be used by the visual + * Function that converts queried data into bar chart data points that will be used by the visual * * @function * @param {VisualUpdateOptions} options - Contains references to the size of the container @@ -74,9 +74,8 @@ IVisualHost is required because when defining individual data points, you will w * the visual had queried. * @param {IVisualHost} host - Contains references to the host which contains services */ -function visualTransform(options: VisualUpdateOptions, host: IVisualHost): BarChartViewModel { - /*Convert dataView to your viewModel*/ +function createSelectorDataPoints(options: VisualUpdateOptions, host: IVisualHost): BarChartDataPoint[] { + /*Convert dataView to bar chart data points*/ } -``` -See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/commit/3c6e8186436b63bf0cf97d2cdd5dde8aa8d08709) for what was added to visualTransform +``` \ No newline at end of file diff --git a/Tutorial/DataBoundObjects.md b/Tutorial/DataBoundObjects.md index 421cb7e..9ecc0fd 100644 --- a/Tutorial/DataBoundObjects.md +++ b/Tutorial/DataBoundObjects.md @@ -9,16 +9,13 @@ See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/commit/ ## Define Object in Capabilities Similar to static objects, we will define another object in the capabilities `colorSelector` is the internal name that will be referenced in the `dataView`. -`displayName` is the name that will be shown on the property pane. `fill` is a `StructuralObjectValue` and is not associated with a primitive type. ```typescript "colorSelector": { - "displayName": "Data Colors", "properties": { "fill": { - "displayName": "Color", "type": { "fill": { "solid": { @@ -69,74 +66,65 @@ export function getCategoricalObjectValue(category: DataViewCategoryColumn, i See [objectEnumerationUtility.ts](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/blob/master/src/objectEnumerationUtility.ts) for source code. ## Defining Default Color and Retrieving Categorical Object from DataView -Each color is now associated with each category inside `dataView`. We will set each data point to its cooresponding color. +Each color is now associated with each category inside options dataView. We will set each data point to its corresponding color. ```typescript + const strokeWidth: number = getColumnStrokeWidth(colorPalette.isHighContrast); + for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { - let defaultColor: Fill = { - solid: { - color: colorPalette.getColor(category.values[i]).value - } - } + const color: string = getColumnColorByIndex(category, i, colorPalette); + + const selectionId: ISelectionId = host.createSelectionIdBuilder() + .withCategory(category, i) + .createSelectionId(); barChartDataPoints.push({ - category: category.values[i], + color, + strokeColor, + strokeWidth, + selectionId, value: dataValue.values[i], - color: getCategoricalObjectValue(category, i, 'colorSelector', 'fill', defaultColor).solid.color, - selectionId: host.createSelectionIdBuilder() - .withCategory(category, i) - .createSelectionId() + category: `${category.values[i]}`, }); } ``` -## Populate Property Pane with `enumerateObjectInstances` -`enumerateObjectInstances` is used to populate the property pane with objects. +## Populate Property Pane with `getFormattingModel` +`getFormattingModel` is used to populate the property pane with objects. +For more information [here](https://learn.microsoft.com/en-us/power-bi/developer/visuals/format-pane) + For this instance, we would like a color picker per category we have. Each category be rendered on the property pane. +We will do this by adding a populate method `populateColorSelector` to create corresponding bar chart data points color selector in format pane after building the data points in `update` method. This `populateColorSelector` method iterate through each data point with the associated color. -We will do this by adding an additional case to the switch statement for `colorSelector` and iterate through each data point with the associated color. +Selection is required to associate the color with a data point. +In visual class: +```typescript + public update(options: VisualUpdateOptions) { + this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(BarChartSettingsModel, options.dataViews); + this.barDataPoints = createSelectorDataPoints(options, this.host); + this.formattingSettings.populateColorSelector(this.barDataPoints); -Selection is required to associate the color with a datapoint. + // ... + } +``` +In formatting settings model: ```typescript -/** - * Enumerates through the objects defined in the capabilities and adds the properties to the format pane - * - * @function - * @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects + /** + * populate colorSelector object categories formatting properties + * @param dataPoints */ -public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration { - let objectName = options.objectName; - let objectEnumeration: VisualObjectInstance[] = []; - - switch(objectName) { - case 'enableAxis': - objectEnumeration.push({ - objectName: objectName, - properties: { - show: this.barChartSettings.enableAxis.show, - }, - selector: null +populateColorSelector(dataPoints: BarChartDataPoint[]) { + const slices: formattingSettings.ColorPicker[] = this.colorSelector.slices; + if (dataPoints) { + dataPoints.forEach(dataPoint => { + slices.push(new formattingSettings.ColorPicker({ + name: "fill", + displayName: dataPoint.category, + value: { value: dataPoint.color }, + selector: dataPoint.selectionId.getSelector(), + })); }); - break; - case 'colorSelector': - for(let barDataPoint of this.barDataPoints) { - objectEnumeration.push({ - objectName: objectName, - displayName: barDataPoint.category, - properties: { - fill: { - solid: { - color: barDataPoint.color - } - } - }, - selector: barDataPoint.selectionId.getSelector() - }); - } - break; - }; - - return objectEnumeration; -} -``` + } + } +``` \ No newline at end of file diff --git a/Tutorial/ExtensibilityUtils.md b/Tutorial/ExtensibilityUtils.md index 7d095f2..a246b9e 100644 --- a/Tutorial/ExtensibilityUtils.md +++ b/Tutorial/ExtensibilityUtils.md @@ -9,6 +9,8 @@ PowerBI provides several tools that help to cover the main needs to build your o 5. [InteractivityUtils](https://www.npmjs.com/package/powerbi-visuals-utils-interactivityutils) is a set of functions and classes for implementation of cross-selection and cross-filtering for PowerBI custom visuals. 6. [FormattingUtils](https://www.npmjs.com/package/powerbi-visuals-utils-formattingutils) are interfaces for creating PowerBI custom visuals. 7. [SVGUtils](https://www.npmjs.com/package/powerbi-visuals-utils-svgutils) is a tool for SVG manipulations for PowerBI custom visuals. +8. [FormattingModelUtils](https://github.com/microsoft/powerbi-visuals-utils-formattingmodel) is a set of classes, interfaces and method help building format pane easily. +9. [OnObjectUtils](https://github.com/microsoft/powerbi-visuals-utils-onobjectutils) provides an easy way for your Power BI custom visual to emit subselections to Power BI, get and render outlines. ### How to install To install the package you should run the following command in the directory with your current custom visual: @@ -47,4 +49,4 @@ After that user can use all available module methods .call(xAxis); ``` -To get more information about SVGItils package, please check the following [documentation](https://github.com/Microsoft/powerbi-visuals-utils-svgutils/) +To get more information about SVGItils package, please check the following [documentation](https://github.com/Microsoft/powerbi-visuals-utils-svgutils/) \ No newline at end of file diff --git a/Tutorial/ExternalLibraries.md b/Tutorial/ExternalLibraries.md index ab548fa..4e39912 100644 --- a/Tutorial/ExternalLibraries.md +++ b/Tutorial/ExternalLibraries.md @@ -40,4 +40,4 @@ Add the library to your `tsconfig.json` file. } ``` -Refer to [this](Typings.md) if you'd like to add typings for your JS file to get intellisense and compile time safety on them. +Refer to [this](Typings.md) if you'd like to add typings for your JS file to get intellisense and compile time safety on them. \ No newline at end of file diff --git a/Tutorial/HighContrastSupport.md b/Tutorial/HighContrastSupport.md index a1ad392..e9a73e7 100644 --- a/Tutorial/HighContrastSupport.md +++ b/Tutorial/HighContrastSupport.md @@ -65,7 +65,7 @@ In Sample Bar Chart, for example, all bars are drawn with 2 pixels thick foregro ![Sample Bar Chart using *Dark #2* color theme](images/HC_sampleBarChart_dark2.png) ![Sample Bar Chart using *White* color theme](images/HC_sampleBarChart_white.png) -Here is one place in the `visualTransform` function that was changed to support high-contrast, it is called as part of rendering during `update`: +Here is one place in the `createSelectorDataPoints` function that was changed to support high-contrast, it is called as part of rendering during `update`: **before** ```typescript @@ -89,6 +89,11 @@ Here is one place in the `visualTransform` function that was changed to support **after** ```typescript + + const colorPalette: ISandboxExtendedColorPalette = host.colorPalette; + const strokeColor: string = getColumnStrokeColor(colorPalette); + const strokeWidth: number = getColumnStrokeWidth(colorPalette.isHighContrast); + for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { const color: string = getColumnColorByIndex(category, i, colorPalette); @@ -125,9 +130,4 @@ Here is one place in the `visualTransform` function that was changed to support return getCategoricalObjectValue(category, index, 'colorSelector', 'fill', defaultColor).solid.color; } -``` - - - - - +``` \ No newline at end of file diff --git a/Tutorial/LaunchURL.md b/Tutorial/LaunchURL.md index b004453..0d61fa7 100644 --- a/Tutorial/LaunchURL.md +++ b/Tutorial/LaunchURL.md @@ -1,12 +1,10 @@ # Opening a URL in a new tab/window Launch URL allows opening a new browser tab (or window), by delegating the actual work to Power BI. -See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/commit/2ecc5cf74b9bc6fbf5c03f84c3ab24841b489d4e) for what was added at this step. - Note: custom visuals are hosted in Power BI inside sandboxed iframes, this prevents opening a new browser tab (or window) in "the usual way", e.g. using `window.open('http://some.link.net','_blank')`. ## Usage -Use the `host.launchUrl()` API call, passing your destenation URL as a string argument: +Use the `host.launchUrl()` API call, passing your destination URL as a string argument: ```typescript this.host.launchUrl('http://some.link.net'); @@ -18,7 +16,7 @@ this.host.launchUrl('http://some.link.net'); ## Best practices 1. For most cases, it is best to only open a link as a response to a user's explicit action. Make it easy for the user to understand that clicking the link or button will result in opening a new tab. Triggering a `launchUrl()` call without a user's action, or as a side effect of a different action can be confusing or frustrating for the user. -2. If the link is not crucial for the proper functioning of the visual, it is recommanded to provide the report's author a way to disable and hide the link. This is especially relevant for special Power BI use-cases, such as embedding a report in a 3rd party application or publishing it to the web. +2. If the link is not crucial for the proper functioning of the visual, it is recommended to provide the report's author a way to disable and hide the link. This is especially relevant for special Power BI use-cases, such as embedding a report in a 3rd party application or publishing it to the web. 3. Avoid Triggering a `launchUrl()` call from inside a loop, the visual's `update` function, or any other frequently recurring code. ## Step by step example @@ -51,11 +49,9 @@ A `showHelpLink` boolean static object was added to `capabilities.json` objects "objects": { //... "generalView": { - "displayName": "General View", "properties": //... "showHelpLink": { - "displayName": "Show Help Button", "type": { "bool": true } @@ -69,10 +65,10 @@ A `showHelpLink` boolean static object was added to `capabilities.json` objects And, in the visual's `update` function, the following lines were added: ```typescript - if (settings.generalView.showHelpLink) { - this.helpLinkElement.classList.remove("hidden"); - } else { - this.helpLinkElement.classList.add("hidden"); - } +this.helpLinkElement + .classed("hidden", !this.formattingSettings.generalView.showHelpLink.value) + .style("border-color", this.formattingSettings.generalView.helpLinkColor) + .style("color", this.formattingSettings.generalView.helpLinkColor); ``` -The `hidden` class is defined in visual.less to control the display of the element. + +The `hidden` class is defined in visual.less to control the display of the element. \ No newline at end of file diff --git a/Tutorial/Locale.md b/Tutorial/Locale.md index d60d826..8795447 100644 --- a/Tutorial/Locale.md +++ b/Tutorial/Locale.md @@ -14,7 +14,7 @@ In the sample we display the current locale in the tooltip. Each of these bar charts was created under different locale (English, Basque and Hindi). -The BarChart contructor now has a `locale` member which is instantiated in the constructor with the host `locale` instance. +The BarChart constructor now has a `locale` member which is instantiated in the constructor with the host `locale` instance. ```typescript private locale: string; @@ -22,7 +22,7 @@ The BarChart contructor now has a `locale` member which is instantiated in the c this.locale = options.host.locale; ``` -A `LocalizaionResources` interface was added, which helps in localizing strings. It defines the required string for each locale, and also the 'defaultValue', which will be displayed if the visual wansn't adapted to this locale.
+A `LocalizationResources` interface was added, which helps in localizing strings. It defines the required string for each locale, and also the 'defaultValue', which will be displayed if the visual wasn't adapted to this locale.
`myResources` is an instance of this interface, which holds the localized strings: ```typescript @@ -45,7 +45,7 @@ module powerbi.extensibility.visual { Getting a localized string is easy using `getLocalizedString`. ```typescript /** - * Returns the localized string in the locale transfared using the key that was given to serch the resources + * Returns the localized string in the locale transferred using the key that was given to search the resources * * @param {string} locale - the locale in which PowerBI is currently running * @param {object} key - specify a key for the string you want localized in your visual @@ -68,3 +68,6 @@ private getTooltipData(value: any): VisualTooltipDataItem[] { }]; } ``` + +## Format Pane Localization +For more info on localization [here](https://learn.microsoft.com/power-bi/developer/visuals/localization) \ No newline at end of file diff --git a/Tutorial/OnObject.md b/Tutorial/OnObject.md new file mode 100644 index 0000000..8593398 --- /dev/null +++ b/Tutorial/OnObject.md @@ -0,0 +1,364 @@ +# Adding OnObject formatting to your Visual +The `IVisual` interface expose `VisualOnObjectFormatting` which we will implement in this tutorial using the `HtmlSubSelectionHelper` and `subSelectionService` exposed on `IVisualHost`. + +```typescript +interface VisualOnObjectFormatting { + getSubSelectionStyles(subSelections: powerbi.visuals.CustomVisualSubSelection[]): powerbi.visuals. SubSelectionStyles | undefined; + getSubSelectionShortcuts(subSelections: powerbi.visuals.CustomVisualSubSelection[], filter: powerbi.visuals.SubSelectionShortcutsKey | undefined): powerbi.visuals.VisualSubSelectionShortcuts | undefined; + getSubSelectables?(filter?: powerbi.visuals.SubSelectionStylesType): powerbi.visuals.CustomVisualSubSelection[] | undefined; + } +``` + +import the `HtmlSubSelectionHelper` and its attributes: +```typescript +import { + HtmlSubSelectableClass, HtmlSubSelectionHelper, SubSelectableDirectEdit as SubSelectableDirectEditAttr, + SubSelectableDisplayNameAttribute, SubSelectableObjectNameAttribute, SubSelectableTypeAttribute +} from 'powerbi-visuals-utils-onobjectutils'; +``` + +To make it easy to use the visual object names let's create an enum containing the visual objects: +```typescript +const enum BarChartObjectNames { + ArcElement = 'arcElement', + ColorSelector = 'colorSelector', + EnableAxis = 'enableAxis', + DirectEdit = 'directEdit' +} +``` + +create a reference interface for the visual objects properties FormattingIds, this will help when implementing the `VisualOnObjectFormatting` methods +```typescript +interface References { + cardUid?: string; + groupUid?: string; + fill?: FormattingId; + font?: FormattingId; + fontColor?: FormattingId; + show?: FormattingId; + fontFamily?: FormattingId; + bold?: FormattingId; + italic?: FormattingId; + underline?: FormattingId; + fontSize?: FormattingId; + position?: FormattingId; + textProperty?: FormattingId; +} +``` + +Create a reference for each visual object you need. + +```typescript +const colorSelectorReferences: References = { + cardUid: 'Visual-colorSelector-card', + groupUid: 'colorSelector-group', + fill: { + objectName: BarChartObjectNames.ColorSelector, + propertyName: 'fill' + } +}; + +const enableAxisReferences: References = { + cardUid: 'Visual-enableAxis-card', + groupUid: 'enableAxis-group', + fill: { + objectName: BarChartObjectNames.EnableAxis, + propertyName: 'fill' + }, + show: { + objectName: BarChartObjectNames.EnableAxis, + propertyName: 'show' + } +}; + +const directEditReferences: References = { + cardUid: 'Visual-directEdit-card', + groupUid: 'directEdit-group', + fontFamily: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'fontFamily' + }, + bold: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'bold' + }, + italic: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'italic' + }, + underline: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'underline' + }, + fontSize: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'fontSize' + }, + fontColor: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'fontColor' + }, + show: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'show' + }, + position: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'position' + }, + textProperty: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'textProperty' + } +}; +``` + +implement the `VisualOnObjectFormatting` methods +Note: When providing a selector, make sure that it is the same selector as provided for the formatting model. +```typescript + private getSubSelectionStyles(subSelections: CustomVisualSubSelection[]): powerbi.visuals.SubSelectionStyles | undefined { + const visualObject = subSelections[0]?.customVisualObjects[0]; + if (visualObject) { + switch (visualObject.objectName) { + case BarChartObjectNames.ColorSelector: + return this.getColorSelectorStyles(subSelections); + case BarChartObjectNames.EnableAxis: + return this.getEnableAxisStyles(); + case BarChartObjectNames.DirectEdit: + return this.getDirectEditStyles(); + } + } + } + private getSubSelectionShortcuts(subSelections: CustomVisualSubSelection[]): VisualSubSelectionShortcuts | undefined { + const visualObject = subSelections[0]?.customVisualObjects[0]; + if (visualObject) { + switch (visualObject.objectName) { + case BarChartObjectNames.ColorSelector: + return this.getColorSelectorShortcuts(subSelections); + case BarChartObjectNames.EnableAxis: + return this.getEnableAxisShortcuts(); + case BarChartObjectNames.DirectEdit: + return this.getDirectEditShortcuts(); + } + } + } + private getSubSelectables?(filter?: powerbi.visuals.SubSelectionStylesType): CustomVisualSubSelection[] | undefined { + return this.subSelectionHelper.getAllSubSelectables(filter); + } + + private getColorSelectorShortcuts(subSelections: CustomVisualSubSelection[]): VisualSubSelectionShortcuts { + const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector(); + return [ + { + type: VisualShortcutType.Reset, + relatedResetFormattingIds: [{ + ...colorSelectorReferences.fill, + selector + }], + }, + { + type: VisualShortcutType.Navigate, + destinationInfo: { cardUid: colorSelectorReferences.cardUid }, + label: 'Color' + } + ]; + } + + private getColorSelectorStyles(subSelections: CustomVisualSubSelection[]): SubSelectionStyles { + const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector(); + return { + type: SubSelectionStylesType.Shape, + fill: { + label: 'Fill', + reference: { + ...colorSelectorReferences.fill, + selector + }, + }, + }; + } + + private getEnableAxisStyles(): SubSelectionStyles { + return { + type: SubSelectionStylesType.Shape, + fill: { + reference: { + ...enableAxisReferences.fill + }, + label: 'Enable Axis' + } + } + } + + private getEnableAxisShortcuts(): VisualSubSelectionShortcuts { + return [ + { + type: VisualShortcutType.Reset, + relatedResetFormattingIds: [{ + ...enableAxisReferences.fill, + }], + excludedResetFormattingIds: [{ + ...enableAxisReferences.show, + }] + }, + { + type: VisualShortcutType.Toggle, + relatedToggledFormattingIds: [{ + ...enableAxisReferences.show + }], + ...enableAxisReferences.show, + disabledLabel: 'Delete', + enabledLabel: 'Delete' + }, + { + type: VisualShortcutType.Navigate, + destinationInfo: { cardUid: enableAxisReferences.cardUid }, + label: 'EnableAxis' + } + ]; + } + + private getDirectEditShortcuts(): VisualSubSelectionShortcuts { + return [ + { + type: VisualShortcutType.Reset, + relatedResetFormattingIds: [ + directEditReferences.bold, + directEditReferences.fontFamily, + directEditReferences.fontSize, + directEditReferences.italic, + directEditReferences.underline, + directEditReferences.fontColor, + directEditReferences.textProperty + ] + }, + { + type: VisualShortcutType.Toggle, + relatedToggledFormattingIds: [{ + ...directEditReferences.show, + }], + ...directEditReferences.show, + disabledLabel: 'Delete', + + }, + { + type: VisualShortcutType.Picker, + ...directEditReferences.position, + label: 'Position' + }, + { + type: VisualShortcutType.Navigate, + destinationInfo: { cardUid: directEditReferences.cardUid }, + label: 'Direct edit' + } + ]; + } + + private getDirectEditStyles(): SubSelectionStyles { + return { + type: powerbi.visuals.SubSelectionStylesType.Text, + fontFamily: { + reference: { + ...directEditReferences.fontFamily + }, + label: 'font' + }, + bold: { + reference: { + ...directEditReferences.bold + }, + label: 'font' + }, + italic: { + reference: { + ...directEditReferences.italic + }, + label: 'font' + }, + underline: { + reference: { + ...directEditReferences.underline + }, + label: 'font' + }, + fontSize: { + reference: { + ...directEditReferences.fontSize + }, + label: 'font' + }, + fontColor: { + reference: { + ...directEditReferences.fontColor + }, + label: 'fontColor' + }, + background: { + reference: { + objectName: 'directEdit', + propertyName: 'background' + }, + label: 'background' + } + }; + } +``` + +in the constuctor create the `HtmlSubSelectionHelper` and provide the get methods in the `visualOnObjectFormatting` +```typescript + this.subSelectionHelper = HtmlSubSelectionHelper.createHtmlSubselectionHelper({ + hostElement: options.element, + subSelectionService: options.host.subSelectionService, + selectionIdCallback: (e) => this.selectionIdCallback(e), + }); + + this.visualOnObjectFormatting = { + getSubSelectionStyles: (subSelections) => this.getSubSelectionStyles(subSelections), + getSubSelectionShortcuts: (subSelections) => this.getSubSelectionShortcuts(subSelections), + getSubSelectables: (filter) => this.getSubSelectables(filter) + }; +``` + +In the visual update: +Add `HtmlSubSelectionHelper` attributes to the relevant element and set their format in the visual update, for example for the `colorSelector` +```typescript + barSelectionMerged + .attr(SubSelectableObjectNameAttribute, 'colorSelector') + .attr(SubSelectableDisplayNameAttribute, (dataPoint: BarChartDataPoint) => this.formattingSettings.colorSelector.slices[dataPoint.index].displayName) + .attr(SubSelectableTypeAttribute, powerbi.visuals.SubSelectionStylesType.Shape) + .classed(HtmlSubSelectableClass, options.formatMode) + .attr("width", xScale.bandwidth()) + .attr("height", d => height - yScale(d.value)) + .attr("y", d => yScale(d.value)) + .attr("x", d => xScale(d.category)) + .style("fill-opacity", opacity) + .style("stroke-opacity", opacity) + .style("fill", (dataPoint: BarChartDataPoint) => dataPoint.color) + .style("stroke", (dataPoint: BarChartDataPoint) => dataPoint.strokeColor) + .style("stroke-width", (dataPoint: BarChartDataPoint) => `${dataPoint.strokeWidth}px`); +``` + +set the formatMode for the `HtmlSubSelectionHelper` +```typescript + this.subSelectionHelper.setFormatMode(options.formatMode); +``` + + +disable data interactivity when the visual in formatMode +```typescript + if (this.formatMode) { + this.removeEventHandlers(barSelectionMerged); + } else { + this.addEventHandlers(barSelectionMerged); + } +``` + +When a subselection is sub-selected we will get it in the update, use `HtmlSubSelectionHelper` to render the subselection outlines +```typescript + const shouldUpdateSubSelection = options.type & (powerbi.VisualUpdateType.Data + | powerbi.VisualUpdateType.Resize + | powerbi.VisualUpdateType.FormattingSubSelectionChange); + if (this.formatMode && shouldUpdateSubSelection) { + this.subSelectionHelper.updateOutlinesFromSubSelections(options.subSelections, true); + } +``` \ No newline at end of file diff --git a/Tutorial/ReportPageTooltips.md b/Tutorial/ReportPageTooltips.md index 4bc775b..0a8d4b6 100644 --- a/Tutorial/ReportPageTooltips.md +++ b/Tutorial/ReportPageTooltips.md @@ -30,15 +30,14 @@ To support displaying report page tooltips, add "tooltips" definition to capabil `roles` optional. Once defined, instructs what data roles will be bound to the selected tooltip option in fields well. -For more information, see the Report Page Tooltips usage guidlines [Report Page Tooltips](https://powerbi.microsoft.com/en-us/blog/power-bi-desktop-march-2018-feature-summary/#tooltips). +For more information, see the Report Page Tooltips usage guidelines [Report Page Tooltips](https://powerbi.microsoft.com/en-us/blog/power-bi-desktop-march-2018-feature-summary/#tooltips). ## Applying report page tooltips For displaying the report page tooltip, upon calling ITooltipService.Show(options: TooltipShowOptions) or ITooltipService.Move(options: TooltipMoveOptions), the PowerBI host will consume the selectionId ('identities' property of 'options' argument above). -Therefore, the SelectionId should represent the selected data (category, series, etc) of the item you hovered above to be retreived by the tooltip. +Therefore, the SelectionId should represent the selected data (category, series, etc) of the item you hovered above to be retrieved by the tooltip. See more on building SelectionId under [Adding Selection and Interactions with Other Visuals](https://github.com/Microsoft/PowerBI-visuals/blob/master/Tutorial/Selection.md) Example of sending the selectionId to tooltip display calls: -![](images/ApplyReportPageTooltip.png) - +![](images/ApplyReportPageTooltip.png) \ No newline at end of file diff --git a/Tutorial/Selection.md b/Tutorial/Selection.md index cef5461..9e65aa2 100644 --- a/Tutorial/Selection.md +++ b/Tutorial/Selection.md @@ -11,22 +11,26 @@ Since each data point is unique, selection must be added to each data point. Add * Interface for BarChart data points. * * @interface - * @property {number} value - Data value for point. + * @property {PrimitiveValue} value - Data value for point. * @property {string} category - Corresponding category of data value. * @property {string} color - Color corresponding to data point. + * @property {string} strokeColor - Stroke color for data point column. + * @property {number} strokeWidth - Stroke width for data point column. * @property {ISelectionId} selectionId - Id assigned to data point for cross filtering * and visual interaction. */ -interface BarChartDataPoint { - value: number; +export interface BarChartDataPoint { + value: PrimitiveValue; category: string; color: string; + strokeColor: string; + strokeWidth: number; selectionId: ISelectionId; -}; +} ``` ## Assigning Selection Ids to Each Data Point -Since we iterate through the data points in `visualTransform` it is also the ideal place to create your selection ids. +Since we iterate through the data points in `createSelectorDataPoints` it is also the ideal place to create your selection ids. The host variable is a `IVisualHost`, which contains services that the visual may use such as color and selection builder. Use the selection builder factory method on `IVisualHost` to create a new selection id. @@ -35,16 +39,23 @@ Since we're making selection only based on the category, we only need to define **NOTE**: A new selection builder must be created per data point. ```typescript -for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { - barChartDataPoints.push({ - category: category.values[i], - value: dataValue.values[i], - color: colorPalette.getColor(category.values[i]).value, - selectionId: host.createSelectionIdBuilder() + + for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { + const color: string = getColumnColorByIndex(category, i, colorPalette); + + const selectionId: ISelectionId = host.createSelectionIdBuilder() .withCategory(category, i) - .createSelectionId() - }); -} + .createSelectionId(); + + barChartDataPoints.push({ + color, + strokeColor, + strokeWidth, + selectionId, + value: dataValue.values[i], + category: `${category.values[i]}`, + }); + } ``` For more information, see the section about using [Selection Id Builder](https://github.com/Microsoft/PowerBI-visuals/blob/master/Visual/Selection.md#creating-selection-ids-selectionidbuilder). @@ -54,25 +65,47 @@ Each bar on the bar chart can be interacted with once a selection id is assigned The bar chart will listen to click events. Use the selection manager factory method on `IVisualHost` to create selection manager. This allow for cross filtering and clearing selections. +Call `syncSelectionState` using selectionManager selectionIds and barSelection: ```typescript -let selectionManager = this.selectionManager; - -//This must be an anonymous function instead of a lambda because -//d3 uses 'this' as the reference to the element that was clicked. -bars.on('click', function(d) { - selectionManager.select(d.selectionId).then((ids: ISelectionId[]) => { - bars.attr({ - 'fill-opacity': ids.length > 0 ? BarChart.Config.transparentOpacity : BarChart.Config.solidOpacity - }); +this.selectionManager = options.host.createSelectionManager(); +this.selectionManager.registerOnSelectCallback(() => { + this.syncSelectionState(this.barSelection, this.selectionManager.getSelectionIds()); +}); - d3.select(this).attr({ - 'fill-opacity': BarChart.Config.solidOpacity +// .... + +private syncSelectionState( + selection: Selection, + selectionIds: ISelectionId[] + ): void { + if (!selection || !selectionIds) { + return; + } + + if (!selectionIds.length) { + const opacity: number = this.formattingSettings.generalView.opacity.value / 100; + selection + .style("fill-opacity", opacity) + .style("stroke-opacity", opacity); + return; + } + + const self: this = this; + + selection.each(function (barDataPoint: BarChartDataPoint) { + const isSelected: boolean = self.isSelectionIdInArray(selectionIds, barDataPoint.selectionId); + + const opacity: number = isSelected + ? BarChart.Config.solidOpacity + : BarChart.Config.transparentOpacity; + + d3Select(this) + .style("fill-opacity", opacity) + .style("stroke-opacity", opacity); }); - }); + } - (d3.event).stopPropagation(); -}); ``` -For more information, see the section about using [Selection Manager](https://github.com/Microsoft/PowerBI-visuals/blob/master/Visual/Selection.md#managing-selection-selectionmanager). +For more information, see the section about using [Selection Manager](https://github.com/Microsoft/PowerBI-visuals/blob/master/Visual/Selection.md#managing-selection-selectionmanager). \ No newline at end of file diff --git a/Tutorial/StaticObjects.md b/Tutorial/StaticObjects.md index a1097ef..f180b67 100644 --- a/Tutorial/StaticObjects.md +++ b/Tutorial/StaticObjects.md @@ -6,26 +6,18 @@ Objects can be toggled on the property pane. ![](images/PropertyPane.png) -See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/commit/7602bb5c34aca97f02ea8e713f841a4ce19929c7) for what was added at this step. - ## Define Object in Capabilities Define an objects property inside your capabilities. This defines the object you plan to display in the property pane. `enableAxis` is the internal name that will be referenced in the `dataView`. -`displayName` is the name that will be shown on the property pane. - `bool` is a `PrimitiveValue` and is typically used with static objects such as text boxes or switches. -**NOTE**: `show` is a special property on `properties`. It enables the switch on the actual object. Since show is a switch, it is typed as a `bool`. - ![](images/ObjectShowProperty.png) ```typescript "objects": { "enableAxis": { - "displayName": "Enable Axis", "properties": { "show": { - "displayName": "Enable Axis", "type": { "bool": true } } } @@ -33,37 +25,153 @@ Define an objects property inside your capabilities. This defines the object you } ``` -For more information, see the section about using [Objects](https://github.com/Microsoft/PowerBI-visuals/blob/master/Capabilities/Objects.md). +For more information, see the section about using [Objects](https://learn.microsoft.com/en-us/power-bi/developer/visuals/capabilities#objects-define-property-pane-options). -## Defining Property Settings +## Defining Formatting Pane Settings Model Although this is optional, it is best to localize most settings onto a single object so that all settings can be easily referenced. ```typescript -/** - * Interface for BarCharts viewmodel. - * - * @interface - * @property {BarChartDataPoint[]} dataPoints - Set of data points the visual will render. - * @property {number} dataMax - Maximum data value in the set of data points. - * @property {BarChartSettings} settings - Object property settings - */ -interface BarChartViewModel { - dataPoints: BarChartDataPoint[]; - dataMax: number; - settings: BarChartSettings; -}; + + private formattingSettingsService: FormattingSettingsService; + private formattingSettings: BarChartSettingsModel; + + + constructor(options: VisualConstructorOptions) { + // ... + + const localizationManager = this.host.createLocalizationManager(); + this.formattingSettingsService = new FormattingSettingsService(localizationManager); + + // ... + } + + + public update(options: VisualUpdateOptions) { + this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(BarChartSettingsModel, options.dataViews); + this.barDataPoints = createSelectorDataPoints(options, this.host); + this.formattingSettings.populateColorSelector(this.barDataPoints); + // ... + } +``` + +Formatting pane settings model : +```typescript + +import Card = formattingSettings.Card; +import Model = formattingSettings.Model; + +class EnableAxisCardSettings extends Card { + show = new formattingSettings.ToggleSwitch({ + name: "show", + displayName: undefined, + value: false, + topLevelToggle: true + }); + + fill = new formattingSettings.ColorPicker({ + name: "fill", + displayName: "Color", + value: { value: "#000000" } + }); + + name: string = "enableAxis"; + displayName: string = "Enable Axis"; + slices = [this.show, this.fill]; +} + + +class ColorSelectorCardSettings extends Card { + name: string = "colorSelector"; + displayName: string = "Data Colors"; + slices = []; +} + +class GeneralViewCardSettings extends Card { + opacity = new formattingSettings.NumUpDown({ + name: "opacity", + displayName: "Bars Opacity", + value: 100, + options: { + minValue: { + type: powerbiVisualsApi.visuals.ValidatorType.Min, + value: 0, + }, + maxValue: { + type: powerbiVisualsApi.visuals.ValidatorType.Max, + value: 100, + } + } + }); + + showHelpLink = new formattingSettings.ToggleSwitch({ + name: "showHelpLink", + displayName: "Show Help Button", + value: false + }); + + name: string = "generalView"; + displayName: string = "General View"; + helpLinkColor: string = "#80B0E0" + slices = [this.opacity, this.showHelpLink]; +} + +class AverageLineCardSettings extends Card { + show = new formattingSettings.ToggleSwitch({ + name: "show", + displayName: undefined, + value: false, + topLevelToggle: true + }); + + fill = new formattingSettings.ColorPicker({ + name: "fill", + displayName: "Color", + value: { value: "#888888" }, + }); + + showDataLabel = new formattingSettings.ToggleSwitch({ + name: "showDataLabel", + displayName: "Data Label", + value: false + }); + + name: string = "averageLine"; + displayName: string = "Average Line"; + analyticsPane: boolean = true; + slices = [this.show, this.fill, this.showDataLabel]; +} /** - * Interface for BarChart settings. - * - * @interface - * @property {{show:boolean}} enableAxis - Object property that allows axis to be enabled. - */ -interface BarChartSettings { - enableAxis: { - show: boolean; - }; +* BarChart formatting settings model class +*/ +export class BarChartSettingsModel extends Model { + enableAxis = new EnableAxisCardSettings(); + colorSelector = new ColorSelectorCardSettings(); + generalView = new GeneralViewCardSettings(); + averageLine = new AverageLineCardSettings(); + cards = [this.enableAxis, this.colorSelector, this.generalView, this.averageLine]; + + /** + * populate colorSelector object categories formatting properties + * @param dataPoints + */ + populateColorSelector(dataPoints: BarChartDataPoint[]) { + let slices = this.colorSelector.slices; + if (dataPoints) { + dataPoints.forEach(dataPoint => { + slices.push(new formattingSettings.ColorPicker({ + name: "fill", + displayName: dataPoint.category, + value: { value: dataPoint.color }, + selector: dataViewWildcard.createDataViewWildcardSelector(dataViewWildcard.DataViewWildcardMatchingOption.InstancesAndTotals), + altConstantSelector: dataPoint.selectionId.getSelector(), + instanceKind: powerbiVisualsApi.VisualEnumerationInstanceKinds.ConstantOrRule + })); + }); + } + } } + ``` ## Defining and Using Object Enumeration Utility Object property values are available as metadata on the `dataView`. However, there is currently no service to help retrieve these properties. @@ -95,65 +203,65 @@ export function getValue(objects: DataViewObjects, objectName: string, proper } ``` -See [objectEnumerationUtility.ts](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/blob/master/src/objectEnumerationUtility.ts) for source code. - ## Retrieving Property Values from DataView -`visualTransform` is the ideal place to manipulate the visual's viewmodel. We will continue this pattern and retrieve the object properties from the `dataView`. +`createSelectorDataPoints` is the ideal place to manipulate the visual's data points. We will continue this pattern and retrieve the object properties from the `dataView`. -Define the default state of the property and use getValue to retrieve the property from the `dataView`. +Define the default state of the property: ```typescript -let defaultSettings: BarChartSettings = { - enableAxis: { - show: false, - } -}; -let barChartSettings: BarChartSettings = { - enableAxis: { - show: getValue(objects, 'enableAxis', 'show', defaultSettings.enableAxis.show), - } +class EnableAxisCardSettings extends Card { + show = new formattingSettings.ToggleSwitch({ + name: "show", + displayName: undefined, + value: false, + topLevelToggle: true + }); + + fill = new formattingSettings.ColorPicker({ + name: "fill", + displayName: "Color", + value: { value: "#000000" } + }); + + name: string = "enableAxis"; + displayName: string = "Enable Axis"; + slices = [this.show, this.fill]; } ``` -## Populate Property Pane with `enumerateObjectInstances` -`enumerateObjectInstances` is an optional method on `IVisual`. Its purpose is to enumerate through all objects and to place them within the property pane. -Each object will be called with `enumerateObjectInstances`. The object's name will be available on `EnumerateVisualObjectInstancesOptions`. +And `formattingSettings` object will get the right value from `dataView` or default value if it wasn't customized in `populateFormattingSettingsModel` method: +```typescript + public update(options: VisualUpdateOptions) { + this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(BarChartSettingsModel, options.dataViews); + this.barDataPoints = createSelectorDataPoints(options, this.host); + this.formattingSettings.populateColorSelector(this.barDataPoints); + + // ... + } +``` -For each object, define the property with its current state. +## Populate Property Pane with `getFormattingModel` +`getFormattingModel` is an optional method on `IVisual`. It returns properties pane formatting model content hierarchies, properties and latest formatting values, Then place them within the property pane properties pane. This method is called once every time we open properties pane or when the user edit any format property. +It can be built with the help of `formattingSettingsService` by calling method `buildFormattingModel`, Where it takes `formattingSettings` and convert it to PBI required `FormattingModel`/ ```typescript -/** - * Enumerates through the objects defined in the capabilities and adds the properties to the format pane - * - * @function - * @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects - */ -public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration { - let objectName = options.objectName; - let objectEnumeration: VisualObjectInstance[] = []; - - switch(objectName) { - case 'enableAxis': - objectEnumeration.push({ - objectName: objectName, - properties: { - show: this.barChartSettings.enableAxis.show, - }, - selector: null - }); - }; - return objectEnumeration; -} + /** + * Returns properties pane formatting model content hierarchies, properties and latest formatting values, Then populate properties pane. + * This method is called once every time we open properties pane or when the user edit any format property. + */ + public getFormattingModel(): powerbiVisualsApi.visuals.FormattingModel { + return this.formattingSettingsService.buildFormattingModel(this.formattingSettings); + } ``` ## Control Property Logic in Update Once an object has been added to the property pane, each toggle will trigger an update. Add specific object logic in `if` blocks. ```typescript -if(settings.enableAxis.show) { + if (this.formattingSettings.enableAxis.show.value) { let margins = BarChart.Config.margins; height -= margins.bottom; } - ``` + ``` \ No newline at end of file diff --git a/Tutorial/Typings.md b/Tutorial/Typings.md index cb2b956..75690fb 100644 --- a/Tutorial/Typings.md +++ b/Tutorial/Typings.md @@ -1,8 +1,7 @@ # Installing Typings for d3 Installing typings will give you access to d3 types so you can utilize typescript types. -See [commit](https://github.com/Microsoft/PowerBI-visuals-sampleBarChart/commit/2bb0f64718864a27e7d4b9c5b1d35d267bba6202) for what was added at this step. -For a more indepth details about typings, visit their repo. [Typings Documentation](https://github.com/typings/typings) +For a more in depth details about typings, visit their repo. [Typings Documentation](https://github.com/typings/typings) ## Install Typings CLI In order to use typings in your project, you must first install typings to your computer. This is a one time installation. diff --git a/capabilities.json b/capabilities.json index 1653d75..9ec0298 100644 --- a/capabilities.json +++ b/capabilities.json @@ -59,16 +59,13 @@ } }, "enableAxis": { - "displayName": "Enable Axis", "properties": { "show": { - "displayName": "Enable Axis", "type": { "bool": true } }, "fill": { - "displayName": "Color", "type": { "fill": { "solid": { @@ -79,11 +76,82 @@ } } }, + "directEdit": { + "properties": { + "show": { + "type": { + "bool": true + } + }, + "textProperty": { + "type": { + "text": true + } + }, + "fontFamily": { + "type": { + "formatting": { + "fontFamily": true + } + } + }, + "fontSize": { + "type": { + "formatting": { + "fontSize": true + } + } + }, + "bold": { + "type": { + "bool": true + } + }, + "italic": { + "type": { + "bool": true + } + }, + "underline": { + "type": { + "bool": true + } + }, + "fontColor": { + "type": { + "fill": { + "solid": { + "color": true + } + } + } + }, + "background": { + "type": { + "fill": { + "solid": { + "color": true + } + } + } + }, + "position": { + "type": { + "enumeration": [ + { + "value": "Left" + }, + { + "value": "Right" + } + ] + } + } + } + }, "colorSelector": { - "displayName": "Data Colors", "properties": { "fill": { - "displayName": "Color", "type": { "fill": { "solid": { @@ -95,16 +163,13 @@ } }, "generalView": { - "displayName": "General View", "properties": { "opacity": { - "displayName": "Bars Opacity", "type": { "integer": true } }, "showHelpLink": { - "displayName": "Show Help Button", "type": { "bool": true } @@ -112,7 +177,6 @@ } }, "averageLine": { - "displayName": "Average Line", "objectCategory": 2, "properties": { "show": { @@ -126,7 +190,6 @@ } }, "fill": { - "displayName": "Color", "type": { "fill": { "solid": { @@ -136,7 +199,6 @@ } }, "showDataLabel": { - "displayName": "Data label", "type": { "bool": true } @@ -144,17 +206,23 @@ } } }, + "supportsOnObjectFormatting": true, + "enablePointerEventsFormatMode": true, "tooltips": { "supportedTypes": { "default": true, "canvas": true }, - "roles": ["Tooltips"], + "roles": [ + "Tooltips" + ], "supportEnhancedTooltips": true }, "supportsLandingPage": false, "drilldown": { - "roles": ["category"] + "roles": [ + "category" + ] }, "privileges": [] -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c73f40d..7cb48ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "visual", "version": "4.0.0", + "dependencies": { + "powerbi-visuals-utils-onobjectutils": "^6.0.1" + }, "devDependencies": { "@types/d3-axis": "^3.0.4", "@types/d3-scale": "^4.0.5", @@ -18,9 +21,10 @@ "d3-selection": "^3.0.0", "eslint": "^8.50.0", "eslint-plugin-powerbi-visuals": "^0.8.1", - "powerbi-visuals-api": "~5.4.0", - "powerbi-visuals-tools": "^5.1.1", + "powerbi-visuals-api": "~5.8.0", + "powerbi-visuals-tools": "^5.3.0", "powerbi-visuals-utils-dataviewutils": "^6.0.1", + "powerbi-visuals-utils-formattingmodel": "6.0.0", "powerbi-visuals-utils-formattingutils": "^6.0.1", "powerbi-visuals-utils-interactivityutils": "^6.0.2", "powerbi-visuals-utils-tooltiputils": "^6.0.1", @@ -296,10 +300,9 @@ } }, "node_modules/@types/d3-selection": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.7.tgz", - "integrity": "sha512-qoj2O7KjfqCobmtFOth8FMvjwMVPUAAmn6xiUbLl1ld7vQCPgffvyV5BBcEFfqWdilAUm+3zciU/3P3vZrUMlg==", - "dev": true + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" }, "node_modules/@types/d3-time": { "version": "1.0.10", @@ -498,15 +501,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz", - "integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", + "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.3", - "@typescript-eslint/types": "6.7.3", - "@typescript-eslint/typescript-estree": "6.7.3", - "@typescript-eslint/visitor-keys": "6.7.3", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", "debug": "^4.3.4" }, "engines": { @@ -525,6 +528,80 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", + "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", + "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", + "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", + "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz", @@ -865,9 +942,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -1026,21 +1103,22 @@ "dev": true }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "node_modules/at-least-node": { @@ -1053,9 +1131,9 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", - "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true, "engines": { "node": ">= 0.4" @@ -1462,13 +1540,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1925,7 +2004,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "dev": true, "engines": { "node": ">=12" } @@ -1963,6 +2041,12 @@ "node": ">=12" } }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1998,6 +2082,20 @@ "node": ">= 10" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -2008,15 +2106,20 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/depd": { @@ -2104,12 +2207,12 @@ } }, "node_modules/domain-browser": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", - "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-5.4.0.tgz", + "integrity": "sha512-p20fsErHHxkrpfqi6BcwUmBWpKCO9oUDAegNqtnzpiS72Zu4uEXqTFlStlOr5dOf53reASN7ab47+P8OLVIVOQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=4" }, "funding": { "url": "https://bevry.me/fund" @@ -2189,66 +2292,12 @@ "errno": "cli.js" } }, - "node_modules/es-abstract": { - "version": "1.18.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", - "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.3", - "is-string": "^1.0.6", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-module-lexer": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", - "dev": true - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2949,11 +2998,14 @@ } } }, - "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } }, "node_modules/forwarded": { "version": "0.2.0", @@ -3014,20 +3066,24 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3118,6 +3174,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3151,40 +3219,43 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, "engines": { - "node": ">= 0.4.0" + "node": ">=8" } }, - "node_modules/has-bigints": { + "node_modules/has-property-descriptors": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -3252,6 +3323,18 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -3315,6 +3398,12 @@ } ] }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -3511,20 +3600,6 @@ "node": ">=0.10.0" } }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -3559,18 +3634,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3583,42 +3646,11 @@ "node": ">=8" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { "node": ">= 0.4" }, @@ -3693,18 +3725,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3714,21 +3734,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -3750,20 +3755,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, "node_modules/is-stream": { @@ -3778,47 +3776,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.7.tgz", - "integrity": "sha512-VxlpTBGknhQ3o7YiVjIhdLU6+oD8dPz/79vvvH4F+S/c8608UCVa9fgDpa1kZgFoUST2DCgacc70UszKgzKuvA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.4", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -4084,12 +4048,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -4108,6 +4066,12 @@ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", "dev": true }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4124,7 +4088,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -4514,14 +4477,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -4916,33 +4879,32 @@ "dev": true }, "node_modules/powerbi-visuals-api": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.4.0.tgz", - "integrity": "sha512-3T7OCBTd6yQtKFt1T+YgQpWGScHByEhOxncWZlNyPVBJBjL+46/CsJL7T6VXOpD0paAYFFS3DWuZOE/q5smPMg==", - "dev": true, + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.8.0.tgz", + "integrity": "sha512-0rcldFiNPn0HSQ3XGdFzRqFofh/UHn0zcGMtcUWXqJIIL3ekhpdRmTdw/OsT9biEHVnBiim3HIhgHHoGr/se/Q==", "dependencies": { "semver": "^7.3.5" } }, "node_modules/powerbi-visuals-tools": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.1.1.tgz", - "integrity": "sha512-kQTWXYI416H5Oh6PPi62Wu7LogGYVnpRjVBJU+dFcXBzzx5MWOecmdY/0Z9Jt2hK6LY0xZuiOxue8mxoOSQQPg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.3.0.tgz", + "integrity": "sha512-d6Y7MuqDqpMyl4r7aAwC85NW2UDYzCUE9hbuUCItrfUdAEHpPGg6eqhPYjZREbbkEmeXNPjGrYra+91XMq6csQ==", "dev": true, "dependencies": { - "@typescript-eslint/parser": "^5.62.0", - "assert": "^2.0.0", - "async": "^3.2.4", + "@typescript-eslint/parser": "^6.12.0", + "assert": "^2.1.0", + "async": "^3.2.5", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "chalk": "^5.3.0", - "commander": "^11.0.0", + "commander": "^11.1.0", "compare-versions": "^6.1.0", "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", "css-loader": "^6.8.1", - "domain-browser": "^4.22.0", + "domain-browser": "^5.4.0", "events": "^3.3.0", "extra-watch-webpack-plugin": "^1.0.3", "fs-extra": "^11.1.1", @@ -4955,12 +4917,13 @@ "lodash.clonedeep": "4.5.0", "lodash.defaults": "4.2.0", "lodash.isequal": "4.5.0", + "lodash.ismatch": "^4.4.0", "mini-css-extract-plugin": "^2.7.6", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", - "powerbi-visuals-webpack-plugin": "4.0.1", + "powerbi-visuals-webpack-plugin": "4.1.0", "process": "^0.11.10", - "punycode": "^2.3.0", + "punycode": "^2.3.1", "querystring-es3": "^0.2.1", "readable-stream": "^4.4.2", "stream-browserify": "^3.0.0", @@ -4968,14 +4931,14 @@ "string_decoder": "^1.3.0", "terser-webpack-plugin": "^5.3.9", "timers-browserify": "^2.0.12", - "ts-loader": "^9.4.4", + "ts-loader": "^9.5.1", "tty-browserify": "^0.0.1", "typescript": "^4.9.5", - "url": "^0.11.1", + "url": "^0.11.3", "util": "^0.12.5", "vm-browserify": "^1.1.2", - "webpack": "^5.88.2", - "webpack-bundle-analyzer": "4.9.0", + "webpack": "^5.89.0", + "webpack-bundle-analyzer": "4.10.1", "webpack-dev-server": "^4.15.1" }, "bin": { @@ -4988,111 +4951,10 @@ "fsevents": "*" } }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/powerbi-visuals-tools/node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" @@ -5136,6 +4998,24 @@ "fsevents": "*" } }, + "node_modules/powerbi-visuals-utils-formattingmodel": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-formattingmodel/-/powerbi-visuals-utils-formattingmodel-6.0.0.tgz", + "integrity": "sha512-2G2qNfcTlJ+aR7CkZDrG75SzH6WO3So/xJFOGtY4bfiVQC4J1ao0juxmIZEyJkCPRV0Sbxg3N0kMxqjOKjdBMA==", + "dev": true, + "dependencies": { + "powerbi-visuals-api": "~5.1.0" + } + }, + "node_modules/powerbi-visuals-utils-formattingmodel/node_modules/powerbi-visuals-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.1.0.tgz", + "integrity": "sha512-ol+h1gF1VRK3n6/wwXR2YMkQsczHU3aiqmqtPl0/KEHPbCBQon/0vcdOVDXFp5QtYj/Ua+LXegD47x8K9FWCzg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + } + }, "node_modules/powerbi-visuals-utils-formattingutils": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-formattingutils/-/powerbi-visuals-utils-formattingutils-6.0.1.tgz", @@ -5171,6 +5051,16 @@ "powerbi-visuals-utils-typeutils": "^6.0.1" } }, + "node_modules/powerbi-visuals-utils-onobjectutils": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-onobjectutils/-/powerbi-visuals-utils-onobjectutils-6.0.1.tgz", + "integrity": "sha512-D7mlH4/6GkhvcDVxmjfqJ9m5yiuRAyTKepJVoMXXoVTo08XabilRgLSPfyWyVyfMGGONObPqiPhmWVhivo3eJg==", + "dependencies": { + "@types/d3-selection": "^3.0.10", + "d3-selection": "^3.0.0", + "powerbi-visuals-api": "~5.8.0" + } + }, "node_modules/powerbi-visuals-utils-svgutils": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-svgutils/-/powerbi-visuals-utils-svgutils-6.0.1.tgz", @@ -5201,9 +5091,9 @@ } }, "node_modules/powerbi-visuals-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/powerbi-visuals-webpack-plugin/-/powerbi-visuals-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-ologdk1TEX2qdvKy9EloIdUf8bxLayvWwoKvyvDbFudMUNOwoiobxaJUXxP11j3DaygrP9PeO0hQoNbVwAuymA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-webpack-plugin/-/powerbi-visuals-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-ttXVVsQYcs6VuoMNZ4SRaK9vdRaIZsU1S16sDSgcnnf5dlaXXiB/j411HACdYLMrU0YUR/vI4QTC7xq52hbMgw==", "dev": true, "dependencies": { "ajv": "6.12.3", @@ -5354,9 +5244,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -5718,7 +5608,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -5798,6 +5687,21 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -5868,14 +5772,14 @@ "dev": true }, "node_modules/sirv": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", - "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", "dev": true, "dependencies": { "@polka/url": "^1.0.0-next.20", "mrmime": "^1.0.0", - "totalist": "^1.0.0" + "totalist": "^3.0.0" }, "engines": { "node": ">= 10" @@ -6028,32 +5932,6 @@ } ] }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6215,9 +6093,9 @@ } }, "node_modules/totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, "engines": { "node": ">=6" @@ -6236,15 +6114,16 @@ } }, "node_modules/ts-loader": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", - "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { "node": ">=12.0.0" @@ -6270,31 +6149,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "node": ">= 8" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", "dev": true }, "node_modules/tty-browserify": { @@ -6341,9 +6208,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6353,21 +6220,6 @@ "node": ">=14.17" } }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -6507,9 +6359,9 @@ } }, "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -6554,20 +6406,23 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz", - "integrity": "sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz", + "integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", - "lodash": "^4.17.20", + "html-escaper": "^2.0.2", + "is-plain-object": "^5.0.0", "opener": "^1.5.2", - "sirv": "^1.0.7", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { @@ -6577,22 +6432,6 @@ "node": ">= 10.13.0" } }, - "node_modules/webpack-bundle-analyzer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/webpack-bundle-analyzer/node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -6782,34 +6621,17 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/which-typed-array": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.6.tgz", - "integrity": "sha512-DdY984dGD5sQ7Tf+x1CkXzdg85b9uEel6nr4UkFg1LoE9OXv3uRuZhe5CoWdawhGACeFpEZXH8fFLQnDhbpm/Q==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.4", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.6" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -6857,8 +6679,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yocto-queue": { "version": "0.1.0", @@ -7091,10 +6912,9 @@ } }, "@types/d3-selection": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.7.tgz", - "integrity": "sha512-qoj2O7KjfqCobmtFOth8FMvjwMVPUAAmn6xiUbLl1ld7vQCPgffvyV5BBcEFfqWdilAUm+3zciU/3P3vZrUMlg==", - "dev": true + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" }, "@types/d3-time": { "version": "1.0.10", @@ -7277,16 +7097,59 @@ } }, "@typescript-eslint/parser": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz", - "integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", + "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.7.3", - "@typescript-eslint/types": "6.7.3", - "@typescript-eslint/typescript-estree": "6.7.3", - "@typescript-eslint/visitor-keys": "6.7.3", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", + "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1" + } + }, + "@typescript-eslint/types": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", + "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", + "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", + "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.13.1", + "eslint-visitor-keys": "^3.4.1" + } + } } }, "@typescript-eslint/scope-manager": { @@ -7555,9 +7418,9 @@ "requires": {} }, "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", "dev": true }, "ajv": { @@ -7678,21 +7541,22 @@ } }, "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "at-least-node": { @@ -7702,9 +7566,9 @@ "dev": true }, "available-typed-arrays": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", - "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true }, "balanced-match": { @@ -8006,13 +7870,14 @@ "dev": true }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "callsites": { @@ -8356,8 +8221,7 @@ "d3-selection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "dev": true + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, "d3-time": { "version": "3.1.0", @@ -8383,6 +8247,12 @@ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", "dev": true }, + "debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -8407,6 +8277,17 @@ "execa": "^5.0.0" } }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -8414,12 +8295,14 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "depd": { @@ -8497,9 +8380,9 @@ } }, "domain-browser": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", - "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-5.4.0.tgz", + "integrity": "sha512-p20fsErHHxkrpfqi6BcwUmBWpKCO9oUDAegNqtnzpiS72Zu4uEXqTFlStlOr5dOf53reASN7ab47+P8OLVIVOQ==", "dev": true }, "duplexer": { @@ -8569,54 +8452,12 @@ "prr": "~1.0.1" } }, - "es-abstract": { - "version": "1.18.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", - "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.3", - "is-string": "^1.0.6", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, "es-module-lexer": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", - "dev": true - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -9156,11 +8997,14 @@ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } }, "forwarded": { "version": "0.2.0", @@ -9205,20 +9049,21 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-stream": { @@ -9279,6 +9124,15 @@ "slash": "^3.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -9306,31 +9160,31 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "get-intrinsic": "^1.2.2" } }, - "has-bigints": { + "has-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, "has-tostringtag": { @@ -9371,6 +9225,15 @@ "minimalistic-assert": "^1.0.1" } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -9426,6 +9289,12 @@ "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", "dev": true }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -9564,17 +9433,6 @@ } } }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, "internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -9597,15 +9455,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -9615,31 +9464,12 @@ "binary-extensions": "^2.0.0" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -9680,27 +9510,12 @@ "define-properties": "^1.1.3" } }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -9713,15 +9528,11 @@ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true }, "is-stream": { "version": "2.0.1", @@ -9729,35 +9540,13 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, "is-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.7.tgz", - "integrity": "sha512-VxlpTBGknhQ3o7YiVjIhdLU6+oD8dPz/79vvvH4F+S/c8608UCVa9fgDpa1kZgFoUST2DCgacc70UszKgzKuvA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.4", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" } }, "is-what": { @@ -9974,12 +9763,6 @@ "p-locate": "^5.0.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -9998,6 +9781,12 @@ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", "dev": true }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -10014,7 +9803,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -10304,14 +10092,14 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, @@ -10588,33 +10376,32 @@ "dev": true }, "powerbi-visuals-api": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.4.0.tgz", - "integrity": "sha512-3T7OCBTd6yQtKFt1T+YgQpWGScHByEhOxncWZlNyPVBJBjL+46/CsJL7T6VXOpD0paAYFFS3DWuZOE/q5smPMg==", - "dev": true, + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.8.0.tgz", + "integrity": "sha512-0rcldFiNPn0HSQ3XGdFzRqFofh/UHn0zcGMtcUWXqJIIL3ekhpdRmTdw/OsT9biEHVnBiim3HIhgHHoGr/se/Q==", "requires": { "semver": "^7.3.5" } }, "powerbi-visuals-tools": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.1.1.tgz", - "integrity": "sha512-kQTWXYI416H5Oh6PPi62Wu7LogGYVnpRjVBJU+dFcXBzzx5MWOecmdY/0Z9Jt2hK6LY0xZuiOxue8mxoOSQQPg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.3.0.tgz", + "integrity": "sha512-d6Y7MuqDqpMyl4r7aAwC85NW2UDYzCUE9hbuUCItrfUdAEHpPGg6eqhPYjZREbbkEmeXNPjGrYra+91XMq6csQ==", "dev": true, "requires": { - "@typescript-eslint/parser": "^5.62.0", - "assert": "^2.0.0", - "async": "^3.2.4", + "@typescript-eslint/parser": "^6.12.0", + "assert": "^2.1.0", + "async": "^3.2.5", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "chalk": "^5.3.0", - "commander": "^11.0.0", + "commander": "^11.1.0", "compare-versions": "^6.1.0", "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", "css-loader": "^6.8.1", - "domain-browser": "^4.22.0", + "domain-browser": "^5.4.0", "events": "^3.3.0", "extra-watch-webpack-plugin": "^1.0.3", "fs-extra": "^11.1.1", @@ -10628,12 +10415,13 @@ "lodash.clonedeep": "4.5.0", "lodash.defaults": "4.2.0", "lodash.isequal": "4.5.0", + "lodash.ismatch": "^4.4.0", "mini-css-extract-plugin": "^2.7.6", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", - "powerbi-visuals-webpack-plugin": "4.0.1", + "powerbi-visuals-webpack-plugin": "4.1.0", "process": "^0.11.10", - "punycode": "^2.3.0", + "punycode": "^2.3.1", "querystring-es3": "^0.2.1", "readable-stream": "^4.4.2", "stream-browserify": "^3.0.0", @@ -10641,74 +10429,21 @@ "string_decoder": "^1.3.0", "terser-webpack-plugin": "^5.3.9", "timers-browserify": "^2.0.12", - "ts-loader": "^9.4.4", + "ts-loader": "^9.5.1", "tty-browserify": "^0.0.1", "typescript": "^4.9.5", - "url": "^0.11.1", + "url": "^0.11.3", "util": "^0.12.5", "vm-browserify": "^1.1.2", - "webpack": "^5.88.2", - "webpack-bundle-analyzer": "4.9.0", + "webpack": "^5.89.0", + "webpack-bundle-analyzer": "4.10.1", "webpack-dev-server": "^4.15.1" }, "dependencies": { - "@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, "commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true }, "readable-stream": { @@ -10741,6 +10476,26 @@ "fsevents": "*" } }, + "powerbi-visuals-utils-formattingmodel": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-formattingmodel/-/powerbi-visuals-utils-formattingmodel-6.0.0.tgz", + "integrity": "sha512-2G2qNfcTlJ+aR7CkZDrG75SzH6WO3So/xJFOGtY4bfiVQC4J1ao0juxmIZEyJkCPRV0Sbxg3N0kMxqjOKjdBMA==", + "dev": true, + "requires": { + "powerbi-visuals-api": "~5.1.0" + }, + "dependencies": { + "powerbi-visuals-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.1.0.tgz", + "integrity": "sha512-ol+h1gF1VRK3n6/wwXR2YMkQsczHU3aiqmqtPl0/KEHPbCBQon/0vcdOVDXFp5QtYj/Ua+LXegD47x8K9FWCzg==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + } + } + }, "powerbi-visuals-utils-formattingutils": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-formattingutils/-/powerbi-visuals-utils-formattingutils-6.0.1.tgz", @@ -10776,6 +10531,16 @@ "powerbi-visuals-utils-typeutils": "^6.0.1" } }, + "powerbi-visuals-utils-onobjectutils": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-onobjectutils/-/powerbi-visuals-utils-onobjectutils-6.0.1.tgz", + "integrity": "sha512-D7mlH4/6GkhvcDVxmjfqJ9m5yiuRAyTKepJVoMXXoVTo08XabilRgLSPfyWyVyfMGGONObPqiPhmWVhivo3eJg==", + "requires": { + "@types/d3-selection": "^3.0.10", + "d3-selection": "^3.0.0", + "powerbi-visuals-api": "~5.8.0" + } + }, "powerbi-visuals-utils-svgutils": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-svgutils/-/powerbi-visuals-utils-svgutils-6.0.1.tgz", @@ -10806,9 +10571,9 @@ } }, "powerbi-visuals-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/powerbi-visuals-webpack-plugin/-/powerbi-visuals-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-ologdk1TEX2qdvKy9EloIdUf8bxLayvWwoKvyvDbFudMUNOwoiobxaJUXxP11j3DaygrP9PeO0hQoNbVwAuymA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-webpack-plugin/-/powerbi-visuals-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-ttXVVsQYcs6VuoMNZ4SRaK9vdRaIZsU1S16sDSgcnnf5dlaXXiB/j411HACdYLMrU0YUR/vI4QTC7xq52hbMgw==", "dev": true, "requires": { "ajv": "6.12.3", @@ -10934,9 +10699,9 @@ } }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "qs": { @@ -11199,7 +10964,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -11269,6 +11033,18 @@ } } }, + "set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -11324,14 +11100,14 @@ "dev": true }, "sirv": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", - "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", "dev": true, "requires": { "@polka/url": "^1.0.0-next.20", "mrmime": "^1.0.0", - "totalist": "^1.0.0" + "totalist": "^3.0.0" } }, "slash": { @@ -11453,26 +11229,6 @@ } } }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11578,9 +11334,9 @@ } }, "totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true }, "ts-api-utils": { @@ -11591,15 +11347,16 @@ "requires": {} }, "ts-loader": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", - "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "requires": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "dependencies": { "chalk": { @@ -11611,6 +11368,12 @@ "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true } } }, @@ -11620,23 +11383,6 @@ "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", "dev": true }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", @@ -11669,23 +11415,11 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -11792,9 +11526,9 @@ } }, "webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", @@ -11837,33 +11571,26 @@ } }, "webpack-bundle-analyzer": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz", - "integrity": "sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz", + "integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==", "dev": true, "requires": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", - "lodash": "^4.17.20", + "html-escaper": "^2.0.2", + "is-plain-object": "^5.0.0", "opener": "^1.5.2", - "sirv": "^1.0.7", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", "ws": "^7.3.1" }, "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -11978,31 +11705,17 @@ "isexe": "^2.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, "which-typed-array": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.6.tgz", - "integrity": "sha512-DdY984dGD5sQ7Tf+x1CkXzdg85b9uEel6nr4UkFg1LoE9OXv3uRuZhe5CoWdawhGACeFpEZXH8fFLQnDhbpm/Q==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.4", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.6" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" } }, "wrappy": { @@ -12027,8 +11740,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yocto-queue": { "version": "0.1.0", diff --git a/package.json b/package.json index 7e7936c..e68d8b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "visual", - "version": "4.0.0", + "version": "4.1.0.0", "scripts": { "pbiviz": "pbiviz", "start": "pbiviz start", @@ -20,12 +20,16 @@ "d3-selection": "^3.0.0", "eslint": "^8.50.0", "eslint-plugin-powerbi-visuals": "^0.8.1", - "powerbi-visuals-api": "~5.4.0", - "powerbi-visuals-tools": "^5.1.1", + "powerbi-visuals-api": "~5.8.0", + "powerbi-visuals-tools": "^5.3.0", "powerbi-visuals-utils-dataviewutils": "^6.0.1", + "powerbi-visuals-utils-formattingmodel": "6.0.0", "powerbi-visuals-utils-formattingutils": "^6.0.1", "powerbi-visuals-utils-interactivityutils": "^6.0.2", "powerbi-visuals-utils-tooltiputils": "^6.0.1", "typescript": "^5.1.6" + }, + "dependencies": { + "powerbi-visuals-utils-onobjectutils": "^6.0.1" } } diff --git a/pbiviz.json b/pbiviz.json index 313591c..06ac37d 100644 --- a/pbiviz.json +++ b/pbiviz.json @@ -4,12 +4,12 @@ "displayName": "barChart", "guid": "PBI_CV_9894B302_1DFF_4A96_ABFE_BF8588197166", "visualClassName": "BarChart", - "version": "4.0.0", + "version": "4.1.0.0", "description": "Sample bar chart", "supportUrl": "pbicvsupport@microsoft.com", "gitHubUrl": "" }, - "apiVersion": "5.4.0", + "apiVersion": "5.8.0", "author": { "name": "Author name", "email": "author@mail.com" diff --git a/src/barChart.ts b/src/barChart.ts index f932f49..40ee050 100644 --- a/src/barChart.ts +++ b/src/barChart.ts @@ -1,26 +1,39 @@ -import "./../style/visual.less"; import { + BaseType, select as d3Select, - Selection as d3Selection, - BaseType + Selection as d3Selection } from "d3-selection"; import { + scaleBand, scaleLinear, - ScaleLinear, - scaleBand + ScaleLinear } from "d3-scale"; import { axisBottom } from "d3-axis"; import powerbiVisualsApi from "powerbi-visuals-api"; +import { createTooltipServiceWrapper, ITooltipServiceWrapper } from "powerbi-visuals-utils-tooltiputils"; +import { FormattingSettingsService } from "powerbi-visuals-utils-formattingmodel"; +import { textMeasurementService, valueFormatter } from "powerbi-visuals-utils-formattingutils"; +import { + HtmlSubSelectableClass, HtmlSubSelectionHelper, SubSelectableDirectEdit as SubSelectableDirectEditAttr, + SubSelectableDisplayNameAttribute, SubSelectableObjectNameAttribute, SubSelectableTypeAttribute +} from 'powerbi-visuals-utils-onobjectutils'; + +import { BarChartSettingsModel } from "./barChartSettingsModel"; +import { getLocalizedString } from "./localization/localizationHelper" +import { getValue, getCategoricalObjectValue } from "./objectEnumerationUtility"; + +import "./../style/visual.less"; + import powerbi = powerbiVisualsApi; type Selection = d3Selection; // powerbi.visuals +import CustomVisualSubSelection = powerbi.visuals.CustomVisualSubSelection; import DataViewCategoryColumn = powerbi.DataViewCategoryColumn; import DataViewObjects = powerbi.DataViewObjects; -import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions; import Fill = powerbi.Fill; import ISandboxExtendedColorPalette = powerbi.extensibility.ISandboxExtendedColorPalette; import ISelectionId = powerbi.visuals.ISelectionId; @@ -28,96 +41,132 @@ import ISelectionManager = powerbi.extensibility.ISelectionManager; import IVisual = powerbi.extensibility.IVisual; import IVisualHost = powerbi.extensibility.visual.IVisualHost; import PrimitiveValue = powerbi.PrimitiveValue; -import VisualObjectInstance = powerbi.VisualObjectInstance; -import VisualObjectInstanceEnumeration = powerbi.VisualObjectInstanceEnumeration; +import SubSelectableDirectEdit = powerbi.visuals.SubSelectableDirectEdit; +import SubSelectableDirectEditStyle = powerbi.visuals.SubSelectableDirectEditStyle; +import SubSelectionStyles = powerbi.visuals.SubSelectionStyles; +import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions; +import VisualShortcutType = powerbi.visuals.VisualShortcutType; +import VisualSubSelectionShortcuts = powerbi.visuals.VisualSubSelectionShortcuts; import VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem; import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions; -import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions; -import VisualEnumerationInstanceKinds = powerbi.VisualEnumerationInstanceKinds; - -import { createTooltipServiceWrapper, ITooltipServiceWrapper } from "powerbi-visuals-utils-tooltiputils"; -import { textMeasurementService, valueFormatter } from "powerbi-visuals-utils-formattingutils"; +import SubSelectionStylesType = powerbi.visuals.SubSelectionStylesType; +import FormattingId = powerbi.visuals.FormattingId; -import { getValue, getCategoricalObjectValue } from "./objectEnumerationUtility"; -import { getLocalizedString } from "./localization/localizationHelper" -import { dataViewWildcard } from "powerbi-visuals-utils-dataviewutils"; - -/** - * Interface for BarCharts viewmodel. - * - * @interface - * @property {BarChartDataPoint[]} dataPoints - Set of data points the visual will render. - * @property {number} dataMax - Maximum data value in the set of data points. - */ -interface BarChartViewModel { - dataPoints: BarChartDataPoint[]; - dataMax: number; - settings: BarChartSettings; -} /** * Interface for BarChart data points. * * @interface - * @property {number} value - Data value for point. + * @property {PrimitiveValue} value - Data value for point. * @property {string} category - Corresponding category of data value. * @property {string} color - Color corresponding to data point. + * @property {string} strokeColor - Stroke color for data point column. + * @property {number} strokeWidth - Stroke width for data point column. * @property {ISelectionId} selectionId - Id assigned to data point for cross filtering * and visual interaction. */ -interface BarChartDataPoint { +export interface BarChartDataPoint { value: PrimitiveValue; category: string; color: string; strokeColor: string; strokeWidth: number; selectionId: ISelectionId; + index: number; format?: string; } -/** - * Interface for BarChart settings. - * - * @interface - * @property {{show:boolean}} enableAxis - Object property that allows axis to be enabled. - * @property {{generalView.opacity:number}} Bars Opacity - Controls opacity of plotted bars, values range between 10 (almost transparent) to 100 (fully opaque, default) - * @property {{generalView.showHelpLink:boolean}} Show Help Button - When TRUE, the plot displays a button which launch a link to documentation. - */ -interface BarChartSettings { - enableAxis: { - show: boolean; - fill: string; - }; - - generalView: { - opacity: number; - showHelpLink: boolean; - helpLinkColor: string; - }; +interface References { + cardUid?: string; + groupUid?: string; + fill?: FormattingId; + font?: FormattingId; + fontColor?: FormattingId; + show?: FormattingId; + fontFamily?: FormattingId; + bold?: FormattingId; + italic?: FormattingId; + underline?: FormattingId; + fontSize?: FormattingId; + position?: FormattingId; + textProperty?: FormattingId; +} - averageLine: { - show: boolean; - displayName: string; - fill: string; - showDataLabel: boolean; - }; +const enum BarChartObjectNames { + ArcElement = 'arcElement', + ColorSelector = 'colorSelector', + EnableAxis = 'enableAxis', + DirectEdit = 'directEdit' } -const defaultSettings: BarChartSettings = { - enableAxis: { - show: false, - fill: "#000000", +const DirectEdit: SubSelectableDirectEdit = { + reference: { + objectName: 'directEdit', + propertyName: 'textProperty' + }, + style: SubSelectableDirectEditStyle.Outline, +}; + +const colorSelectorReferences: References = { + cardUid: 'Visual-colorSelector-card', + groupUid: 'colorSelector-group', + fill: { + objectName: BarChartObjectNames.ColorSelector, + propertyName: 'fill' + } +}; + +const enableAxisReferences: References = { + cardUid: 'Visual-enableAxis-card', + groupUid: 'enableAxis-group', + fill: { + objectName: BarChartObjectNames.EnableAxis, + propertyName: 'fill' }, - generalView: { - opacity: 100, - showHelpLink: false, - helpLinkColor: "#80B0E0", + show: { + objectName: BarChartObjectNames.EnableAxis, + propertyName: 'show' + } +}; + +const directEditReferences: References = { + cardUid: 'Visual-directEdit-card', + groupUid: 'directEdit-group', + fontFamily: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'fontFamily' + }, + bold: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'bold' + }, + italic: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'italic' + }, + underline: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'underline' + }, + fontSize: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'fontSize' + }, + fontColor: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'fontColor' + }, + show: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'show' + }, + position: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'position' }, - averageLine: { - show: false, - displayName: "Average Line", - fill: "#888888", - showDataLabel: false + textProperty: { + objectName: BarChartObjectNames.DirectEdit, + propertyName: 'textProperty' } }; @@ -130,13 +179,9 @@ const defaultSettings: BarChartSettings = { * the visual had queried. * @param {IVisualHost} host - Contains references to the host which contains services */ -function visualTransform(options: VisualUpdateOptions, host: IVisualHost): BarChartViewModel { +function createSelectorDataPoints(options: VisualUpdateOptions, host: IVisualHost): BarChartDataPoint[] { + const barChartDataPoints: BarChartDataPoint[] = [] const dataViews = options.dataViews; - const viewModel: BarChartViewModel = { - dataPoints: [], - dataMax: 0, - settings: {} - }; if (!dataViews || !dataViews[0] @@ -145,39 +190,20 @@ function visualTransform(options: VisualUpdateOptions, host: IVisualHost): BarCh || !dataViews[0].categorical.categories[0].source || !dataViews[0].categorical.values ) { - return viewModel; + return barChartDataPoints; } const categorical = dataViews[0].categorical; const category = categorical.categories[0]; const dataValue = categorical.values[0]; - const barChartDataPoints: BarChartDataPoint[] = []; - let dataMax: number = 0; + //let dataMax: number = 0; const colorPalette: ISandboxExtendedColorPalette = host.colorPalette; - const objects = dataViews[0].metadata.objects; + //const objects = dataViews[0].metadata.objects; const strokeColor: string = getColumnStrokeColor(colorPalette); - const barChartSettings: BarChartSettings = { - enableAxis: { - show: getValue(objects, 'enableAxis', 'show', defaultSettings.enableAxis.show), - fill: getAxisTextFillColor(objects, colorPalette, defaultSettings.enableAxis.fill), - }, - generalView: { - opacity: getValue(objects, 'generalView', 'opacity', defaultSettings.generalView.opacity), - showHelpLink: getValue(objects, 'generalView', 'showHelpLink', defaultSettings.generalView.showHelpLink), - helpLinkColor: strokeColor, - }, - averageLine: { - show: getValue(objects, 'averageLine', 'show', defaultSettings.averageLine.show), - displayName: getValue(objects, 'averageLine', 'displayName', defaultSettings.averageLine.displayName), - fill: getValue(objects, 'averageLine', 'fill', defaultSettings.averageLine.fill), - showDataLabel: getValue(objects, 'averageLine', 'showDataLabel', defaultSettings.averageLine.showDataLabel), - }, - }; - const strokeWidth: number = getColumnStrokeWidth(colorPalette.isHighContrast); for (let i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++) { @@ -194,17 +220,12 @@ function visualTransform(options: VisualUpdateOptions, host: IVisualHost): BarCh selectionId, value: dataValue.values[i], category: `${category.values[i]}`, + index: i, format: dataValue.objects ? dataValue.objects[i].general.formatString : null, }); } - dataMax = dataValue.maxLocal; - - return { - dataPoints: barChartDataPoints, - dataMax: dataMax, - settings: barChartSettings, - }; + return barChartDataPoints; } function getColumnColorByIndex( @@ -265,24 +286,30 @@ function getAxisTextFillColor( } export class BarChart implements IVisual { - private svg: Selection; - private host: IVisualHost; - private selectionManager: ISelectionManager; + private averageLine: Selection; private barContainer: Selection; - private xAxis: Selection; private barDataPoints: BarChartDataPoint[]; - private barChartSettings: BarChartSettings; - private tooltipServiceWrapper: ITooltipServiceWrapper; - private locale: string; - private helpLinkElement: Selection; private element: HTMLElement; + private formattingSettingsService: FormattingSettingsService; + private formattingSettings: BarChartSettingsModel; + private helpLinkElement: Selection; + private host: IVisualHost; private isLandingPageOn: boolean; - private LandingPageRemoved: boolean; private LandingPage: Selection; - private averageLine: Selection; - + private LandingPageRemoved: boolean; + private locale: string; + private selectionManager: ISelectionManager; + private svg: Selection; + private tooltipServiceWrapper: ITooltipServiceWrapper; + private xAxis: Selection; private barSelection: Selection; + private subSelectionHelper: HtmlSubSelectionHelper; + private formatMode: boolean = false; + private directEditElement: Selection; + private visualDirectEditSubSelection = JSON.stringify(DirectEdit); + public visualOnObjectFormatting?: powerbi.extensibility.visual.VisualOnObjectFormatting; + static Config = { xScalePadding: 0.1, solidOpacity: 1, @@ -316,6 +343,16 @@ export class BarChart implements IVisual { this.tooltipServiceWrapper = createTooltipServiceWrapper(this.host.tooltipService, options.element); + //Creating the formatting settings service. + const localizationManager = this.host.createLocalizationManager(); + this.formattingSettingsService = new FormattingSettingsService(localizationManager); + + this.subSelectionHelper = HtmlSubSelectionHelper.createHtmlSubselectionHelper({ + hostElement: options.element, + subSelectionService: options.host.subSelectionService, + selectionIdCallback: (e) => this.selectionIdCallback(e), + }); + this.svg = d3Select(options.element) .append('svg') .classed('barChart', true); @@ -335,6 +372,17 @@ export class BarChart implements IVisual { this.helpLinkElement = d3Select(helpLinkElement); + //create direct edit box + const directEditDiv = this.creatDirectEditElement(); + options.element.appendChild(directEditDiv); + this.directEditElement = d3Select(directEditDiv); + + this.visualOnObjectFormatting = { + getSubSelectionStyles: (subSelections) => this.getSubSelectionStyles(subSelections), + getSubSelectionShortcuts: (subSelections) => this.getSubSelectionShortcuts(subSelections), + getSubSelectables: (filter) => this.getSubSelectables(filter) + }; + this.handleContextMenu(); } @@ -347,11 +395,12 @@ export class BarChart implements IVisual { * the visual had queried. */ public update(options: VisualUpdateOptions) { - const viewModel: BarChartViewModel = visualTransform(options, this.host); - const settings = this.barChartSettings = viewModel.settings; - this.barDataPoints = viewModel.dataPoints; // Turn on landing page in capabilities and remove comment to turn on landing page! // this.HandleLandingPage(options); + this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(BarChartSettingsModel, options.dataViews?.[0]); + this.barDataPoints = createSelectorDataPoints(options, this.host); + this.formattingSettings.populateColorSelector(this.barDataPoints); + this.formatMode = options.formatMode; const width = options.viewport.width; let height = options.viewport.height; @@ -359,26 +408,27 @@ export class BarChart implements IVisual { .attr("width", width) .attr("height", height); - if (settings.enableAxis.show) { + if (this.formattingSettings.enableAxis.show.value) { const margins = BarChart.Config.margins; height -= margins.bottom; } this.helpLinkElement - .classed("hidden", !settings.generalView.showHelpLink) - .style("border-color", settings.generalView.helpLinkColor) - .style("color", settings.generalView.helpLinkColor); + .classed("hidden", !this.formattingSettings.generalView.showHelpLink.value) + .style("border-color", this.formattingSettings.generalView.helpLinkColor) + .style("color", this.formattingSettings.generalView.helpLinkColor); + this.updateDirectEditElementFormat(); this.xAxis .style("font-size", Math.min(height, width) * BarChart.Config.xAxisFontMultiplier) - .style("fill", settings.enableAxis.fill); + .style("fill", this.formattingSettings.enableAxis.fill.value.value); const yScale = scaleLinear() - .domain([0, viewModel.dataMax]) + .domain([0, options.dataViews[0].categorical.values[0].maxLocal]) .range([height, 0]); const xScale = scaleBand() - .domain(viewModel.dataPoints.map(d => d.category)) + .domain(this.barDataPoints.map(d => d.category)) .rangeRound([0, width]) .padding(0.2); @@ -389,10 +439,15 @@ export class BarChart implements IVisual { .attr("color", getAxisTextFillColor( colorObjects, this.host.colorPalette, - defaultSettings.enableAxis.fill + this.formattingSettings.enableAxis.fill.value.value )); - const textNodes = this.xAxis.selectAll("text") + const textNodes = this.xAxis.selectAll("text"); + textNodes + .attr(SubSelectableObjectNameAttribute, 'enableAxis') + .attr(SubSelectableDisplayNameAttribute, 'x-Axis') + .attr(SubSelectableTypeAttribute, powerbi.visuals.SubSelectionStylesType.Shape) + .classed(HtmlSubSelectableClass, options.formatMode && this.formattingSettings.enableAxis.show.value); BarChart.wordBreak(textNodes, xScale.bandwidth(), height); this.handleAverageLineUpdate(height, width, yScale); @@ -407,8 +462,12 @@ export class BarChart implements IVisual { barSelectionMerged.classed('bar', true); - const opacity: number = viewModel.settings.generalView.opacity / 100; + const opacity: number = this.formattingSettings.generalView.opacity.value / 100; barSelectionMerged + .attr(SubSelectableObjectNameAttribute, 'colorSelector') + .attr(SubSelectableDisplayNameAttribute, (dataPoint: BarChartDataPoint) => this.formattingSettings.colorSelector.slices[dataPoint.index].displayName) + .attr(SubSelectableTypeAttribute, powerbi.visuals.SubSelectionStylesType.Shape) + .classed(HtmlSubSelectableClass, options.formatMode) .attr("width", xScale.bandwidth()) .attr("height", d => height - yScale(d.value)) .attr("y", d => yScale(d.value)) @@ -420,34 +479,64 @@ export class BarChart implements IVisual { .style("stroke-width", (dataPoint: BarChartDataPoint) => `${dataPoint.strokeWidth}px`); this.tooltipServiceWrapper.addTooltip(barSelectionMerged, - (datapoint: BarChartDataPoint) => this.getTooltipData(datapoint), - (datapoint: BarChartDataPoint) => datapoint.selectionId + (dataPoint: BarChartDataPoint) => this.getTooltipData(dataPoint), + (dataPoint: BarChartDataPoint) => dataPoint.selectionId ); this.syncSelectionState( barSelectionMerged, this.selectionManager.getSelectionIds() ); + if (this.formatMode) { + this.removeEventHandlers(barSelectionMerged); + } else { + this.addEventHandlers(barSelectionMerged); + } - barSelectionMerged.on('click', (event: Event, datum: BarChartDataPoint) => { - // Allow selection only if the visual is rendered in a view that supports interactivity (e.g. Report) - if (this.host.hostCapabilities.allowInteractions) { - const isCtrlPressed: boolean = (event).ctrlKey; + this.subSelectionHelper.setFormatMode(options.formatMode); + const shouldUpdateSubSelection = options.type & (powerbi.VisualUpdateType.Data + | powerbi.VisualUpdateType.Resize + | powerbi.VisualUpdateType.FormattingSubSelectionChange); + if (this.formatMode && shouldUpdateSubSelection) { + this.subSelectionHelper.updateOutlinesFromSubSelections(options.subSelections, true); + } - this.selectionManager - .select(datum.selectionId, isCtrlPressed) - .then((ids: ISelectionId[]) => { - this.syncSelectionState(barSelectionMerged, ids); - }); - (event).stopPropagation(); - } - }); this.barSelection .exit() .remove(); this.handleClick(barSelectionMerged); } + private removeEventHandlers(barSelectionMerged: d3Selection) { + barSelectionMerged.on('click', null); + this.svg.on('click', null); + this.svg.on('contextmenu', null); + } + + private addEventHandlers(barSelectionMerged: d3Selection) { + this.handleBarClick(barSelectionMerged); + this.handleClick(barSelectionMerged); + this.handleContextMenu(); + } + + private updateDirectEditElementFormat() { + this.directEditElement + .classed('direct-edit', true) + .classed('hidden', !this.formattingSettings.directEditSettings.show.value) + .classed(HtmlSubSelectableClass, this.formatMode && this.formattingSettings.directEditSettings.show.value) + .attr(SubSelectableObjectNameAttribute, 'directEdit') + .attr(SubSelectableDisplayNameAttribute, 'Direct Edit') + .attr(SubSelectableDirectEditAttr, this.visualDirectEditSubSelection) + .style('font-family', this.formattingSettings.directEditSettings.font.fontFamily.value) + .style('color', this.formattingSettings.directEditSettings.fontColor.value.value) + .style('font-style', this.formattingSettings.directEditSettings.font.italic.value ? 'italic' : 'normal') + .style('text-decoration', this.formattingSettings.directEditSettings.font.underline.value ? 'underline' : 'none') + .style('font-weight', this.formattingSettings.directEditSettings.font.bold.value ? 'bold' : 'normal') + .style('right', this.formattingSettings.directEditSettings.position.value.value === 'Right' ? '12px' : '60px') + .style('background-color', this.formattingSettings.directEditSettings.background.value.value) + .style('font-size', `${this.formattingSettings.directEditSettings.font.fontSize.value}px`) + .text(this.formattingSettings.directEditSettings.textProperty.value); + } private static wordBreak( textNodes: Selection, allowedWidth: number, @@ -461,6 +550,22 @@ export class BarChart implements IVisual { }); } + private handleBarClick(barSelectionMerged: Selection) { + barSelectionMerged.on('click', (event: Event, datum: BarChartDataPoint) => { + // Allow selection only if the visual is rendered in a view that supports interactivity (e.g. Report) + if (this.host.hostCapabilities.allowInteractions) { + const isCtrlPressed: boolean = (event).ctrlKey; + + this.selectionManager + .select(datum.selectionId, isCtrlPressed) + .then((ids: ISelectionId[]) => { + this.syncSelectionState(barSelectionMerged, ids); + }); + event.stopPropagation(); + } + }); + } + private handleClick(barSelection: Selection) { // Clear selection when clicking outside a bar this.svg.on('click', () => { @@ -496,7 +601,7 @@ export class BarChart implements IVisual { } if (!selectionIds.length) { - const opacity: number = this.barChartSettings.generalView.opacity / 100; + const opacity: number = this.formattingSettings.generalView.opacity.value / 100; selection .style("fill-opacity", opacity) .style("stroke-opacity", opacity); @@ -529,85 +634,217 @@ export class BarChart implements IVisual { } /** - * Enumerates through the objects defined in the capabilities and adds the properties to the format pane - * - * @function - * @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects + * Returns properties pane formatting model content hierarchies, properties and latest formatting values, Then populate properties pane. + * This method is called once every time we open properties pane or when the user edit any format property. */ - public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration { - const objectName = options.objectName; - const objectEnumeration: VisualObjectInstance[] = []; - - if (!this.barChartSettings || - !this.barChartSettings.enableAxis || - !this.barDataPoints) { - return objectEnumeration; + public getFormattingModel(): powerbiVisualsApi.visuals.FormattingModel { + return this.formattingSettingsService.buildFormattingModel(this.formattingSettings); + } + + private getSubSelectionStyles(subSelections: CustomVisualSubSelection[]): powerbi.visuals.SubSelectionStyles | undefined { + const visualObject = subSelections[0]?.customVisualObjects[0]; + if (visualObject) { + switch (visualObject.objectName) { + case BarChartObjectNames.ColorSelector: + return this.getColorSelectorStyles(subSelections); + case BarChartObjectNames.EnableAxis: + return this.getEnableAxisStyles(); + case BarChartObjectNames.DirectEdit: + return this.getDirectEditStyles(); + } + } + } + private getSubSelectionShortcuts(subSelections: CustomVisualSubSelection[]): VisualSubSelectionShortcuts | undefined { + const visualObject = subSelections[0]?.customVisualObjects[0]; + if (visualObject) { + switch (visualObject.objectName) { + case BarChartObjectNames.ColorSelector: + return this.getColorSelectorShortcuts(subSelections); + case BarChartObjectNames.EnableAxis: + return this.getEnableAxisShortcuts(); + case BarChartObjectNames.DirectEdit: + return this.getDirectEditShortcuts(); + } } + } + private getSubSelectables?(filter?: powerbi.visuals.SubSelectionStylesType): CustomVisualSubSelection[] | undefined { + return this.subSelectionHelper.getAllSubSelectables(filter); + } - switch (objectName) { - case 'enableAxis': - objectEnumeration.push({ - objectName: objectName, - properties: { - show: this.barChartSettings.enableAxis.show, - fill: this.barChartSettings.enableAxis.fill, - }, - selector: null - }); - break; - case 'colorSelector': - for (const barDataPoint of this.barDataPoints) { - objectEnumeration.push({ - objectName: objectName, - displayName: barDataPoint.category, - properties: { - fill: { - solid: { - color: barDataPoint.color - } - } - }, - propertyInstanceKind: { - fill: VisualEnumerationInstanceKinds.ConstantOrRule - }, - altConstantValueSelector: barDataPoint.selectionId.getSelector(), - selector: dataViewWildcard.createDataViewWildcardSelector(dataViewWildcard.DataViewWildcardMatchingOption.InstancesAndTotals) - }); - } - break; - case 'generalView': - objectEnumeration.push({ - objectName: objectName, - properties: { - opacity: this.barChartSettings.generalView.opacity, - showHelpLink: this.barChartSettings.generalView.showHelpLink - }, - validValues: { - opacity: { - numberRange: { - min: 10, - max: 100 - } - } - }, - selector: null - }); - break; - case 'averageLine': - objectEnumeration.push({ - objectName: objectName, - properties: { - show: this.barChartSettings.averageLine.show, - displayName: this.barChartSettings.averageLine.displayName, - fill: this.barChartSettings.averageLine.fill, - showDataLabel: this.barChartSettings.averageLine.showDataLabel - }, - selector: null - }); + private getColorSelectorShortcuts(subSelections: CustomVisualSubSelection[]): VisualSubSelectionShortcuts { + const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector(); + return [ + { + type: VisualShortcutType.Reset, + relatedResetFormattingIds: [{ + ...colorSelectorReferences.fill, + selector + }], + }, + { + type: VisualShortcutType.Navigate, + destinationInfo: { cardUid: colorSelectorReferences.cardUid }, + label: 'Color' + } + ]; + } + + private getColorSelectorStyles(subSelections: CustomVisualSubSelection[]): SubSelectionStyles { + const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector(); + return { + type: SubSelectionStylesType.Shape, + fill: { + label: 'Fill', + reference: { + ...colorSelectorReferences.fill, + selector + }, + }, + }; + } + + private getEnableAxisStyles(): SubSelectionStyles { + return { + type: SubSelectionStylesType.Shape, + fill: { + reference: { + ...enableAxisReferences.fill + }, + label: 'Enable Axis' + } + } + } + + private getEnableAxisShortcuts(): VisualSubSelectionShortcuts { + return [ + { + type: VisualShortcutType.Reset, + relatedResetFormattingIds: [{ + ...enableAxisReferences.fill, + }], + excludedResetFormattingIds: [{ + ...enableAxisReferences.show, + }] + }, + { + type: VisualShortcutType.Toggle, + relatedToggledFormattingIds: [{ + ...enableAxisReferences.show + }], + ...enableAxisReferences.show, + disabledLabel: 'Delete', + enabledLabel: 'Delete' + }, + { + type: VisualShortcutType.Navigate, + destinationInfo: { cardUid: enableAxisReferences.cardUid }, + label: 'EnableAxis' + } + ]; + } + + private getDirectEditShortcuts(): VisualSubSelectionShortcuts { + return [ + { + type: VisualShortcutType.Reset, + relatedResetFormattingIds: [ + directEditReferences.bold, + directEditReferences.fontFamily, + directEditReferences.fontSize, + directEditReferences.italic, + directEditReferences.underline, + directEditReferences.fontColor, + directEditReferences.textProperty + ] + }, + { + type: VisualShortcutType.Toggle, + relatedToggledFormattingIds: [{ + ...directEditReferences.show, + }], + ...directEditReferences.show, + disabledLabel: 'Delete', + + }, + { + type: VisualShortcutType.Picker, + ...directEditReferences.position, + label: 'Position' + }, + { + type: VisualShortcutType.Navigate, + destinationInfo: { cardUid: directEditReferences.cardUid }, + label: 'Direct edit' + } + ]; + } + + private getDirectEditStyles(): SubSelectionStyles { + return { + type: powerbi.visuals.SubSelectionStylesType.Text, + fontFamily: { + reference: { + ...directEditReferences.fontFamily + }, + label: 'font' + }, + bold: { + reference: { + ...directEditReferences.bold + }, + label: 'font' + }, + italic: { + reference: { + ...directEditReferences.italic + }, + label: 'font' + }, + underline: { + reference: { + ...directEditReferences.underline + }, + label: 'font' + }, + fontSize: { + reference: { + ...directEditReferences.fontSize + }, + label: 'font' + }, + fontColor: { + reference: { + ...directEditReferences.fontColor + }, + label: 'fontColor' + }, + background: { + reference: { + objectName: 'directEdit', + propertyName: 'background' + }, + label: 'background' + } + }; + } + + public selectionIdCallback(e: Element): ISelectionId { + const elementType: string = d3Select(e).attr(SubSelectableObjectNameAttribute); + let selectionId: ISelectionId = undefined; + + switch (elementType) { + case BarChartObjectNames.ColorSelector: + selectionId = d3Select(e).datum().selectionId; break; } - return objectEnumeration; + return selectionId; + } + + private creatDirectEditElement(): Element { + const element = document.createElement('div'); + element.setAttribute('class', 'direct-edit'); + return element; } /** @@ -709,13 +946,13 @@ export class BarChart implements IVisual { private handleAverageLineUpdate(height: number, width: number, yScale: ScaleLinear) { const average = this.calculateAverage(); const fontSize = Math.min(height, width) * BarChart.Config.xAxisFontMultiplier; - const chosenColor = this.getColorValue(this.barChartSettings.averageLine.fill); - // If there's no room to place lable above line, place it below + const chosenColor = this.getColorValue(this.formattingSettings.averageLine.fill.value.value); + // If there's no room to place label above line, place it below const labelYOffset = fontSize * ((yScale(average) > fontSize * 1.5) ? -0.5 : 1.5); this.averageLine .style("font-size", fontSize) - .style("display", (this.barChartSettings.averageLine.show) ? "initial" : "none") + .style("display", (this.formattingSettings.averageLine.show.value) ? "initial" : "none") .attr("transform", "translate(0, " + Math.round(yScale(average)) + ")"); this.averageLine.select("#averageLine") @@ -728,7 +965,7 @@ export class BarChart implements IVisual { this.averageLine.select("#averageLineLabel") .text("Average: " + average.toFixed(2)) .attr("transform", "translate(0, " + labelYOffset + ")") - .style("fill", this.barChartSettings.averageLine.showDataLabel ? chosenColor : "none"); + .style("fill", this.formattingSettings.averageLine.showDataLabel.value ? chosenColor : "none"); } private calculateAverage(): number { diff --git a/src/barChartSettingsModel.ts b/src/barChartSettingsModel.ts new file mode 100644 index 0000000..4984840 --- /dev/null +++ b/src/barChartSettingsModel.ts @@ -0,0 +1,192 @@ +import powerbiVisualsApi from "powerbi-visuals-api"; +import { formattingSettings } from "powerbi-visuals-utils-formattingmodel"; +import { BarChartDataPoint } from "./barChart"; + +import Card = formattingSettings.SimpleCard; +import Model = formattingSettings.Model; + +class EnableAxisCardSettings extends Card { + show = new formattingSettings.ToggleSwitch({ + name: "show", + displayName: undefined, + value: false, + }); + + fill = new formattingSettings.ColorPicker({ + name: "fill", + displayName: "Color", + value: { value: "#000000" } + }); + topLevelSlice = this.show; + name: string = "enableAxis"; + displayName: string = "Enable Axis"; + slices = [this.fill]; +} + + +class ColorSelectorCardSettings extends Card { + name: string = "colorSelector"; + displayName: string = "Data Colors"; + slices = []; +} + +class GeneralViewCardSettings extends Card { + opacity = new formattingSettings.NumUpDown({ + name: "opacity", + displayName: "Bars Opacity", + value: 100, + options: { + minValue: { + type: powerbiVisualsApi.visuals.ValidatorType.Min, + value: 0, + }, + maxValue: { + type: powerbiVisualsApi.visuals.ValidatorType.Max, + value: 100, + } + } + }); + + showHelpLink = new formattingSettings.ToggleSwitch({ + name: "showHelpLink", + displayName: "Show Help Button", + value: false + }); + + name: string = "generalView"; + displayName: string = "General View"; + helpLinkColor: string = "#80B0E0" + slices = [this.opacity, this.showHelpLink]; +} + +class AverageLineCardSettings extends Card { + show = new formattingSettings.ToggleSwitch({ + name: "show", + displayName: undefined, + value: false, + }); + + fill = new formattingSettings.ColorPicker({ + name: "fill", + displayName: "Color", + value: { value: "#888888" }, + }); + + showDataLabel = new formattingSettings.ToggleSwitch({ + name: "showDataLabel", + displayName: "Data Label", + value: false + }); + + topLevelSlice = this.show; + name: string = "averageLine"; + displayName: string = "Average Line"; + analyticsPane: boolean = true; + slices = [this.show, this.fill, this.showDataLabel]; +} + +class DirectEditSettings extends Card { + displayName = 'Direct Edit'; + name = 'directEdit'; + private minFontSize: number = 8; + private defaultFontSize: number = 11; + private positionOptions: powerbiVisualsApi.IEnumMember[] = [{ displayName: 'Right', value: 'Right' }, { displayName: 'Left', value: 'Left' }] + show = new formattingSettings.ToggleSwitch({ + name: "show", + displayName: undefined, + value: true, + }); + + topLevelSlice = this.show; + textProperty = new formattingSettings.TextInput({ + displayName: "Text Property", + name: "textProperty", + value: "What is your quest?", + placeholder: "" + }); + + position = new formattingSettings.ItemDropdown({ + name: 'position', + displayName: 'Position', + items: this.positionOptions, + value: this.positionOptions[0] + }); + + font = new formattingSettings.FontControl({ + name: "font", + displayName: 'Font', + fontFamily: new formattingSettings.FontPicker({ + name: "fontFamily", + displayName: "Font Family", + value: "Segoe UI, wf_segoe-ui_normal, helvetica, arial, sans-serif" + }), + fontSize: new formattingSettings.NumUpDown({ + name: "fontSize", + displayName: "Font Size", + value: this.defaultFontSize, + options: { + minValue: { + type: powerbi.visuals.ValidatorType.Min, + value: this.minFontSize, + } + } + }), + bold: new formattingSettings.ToggleSwitch({ + name: 'bold', + displayName: "bold", + value: true + }), + italic: new formattingSettings.ToggleSwitch({ + name: 'italic', + displayName: "italic", + value: true + }), + underline: new formattingSettings.ToggleSwitch({ + name: 'underline', + displayName: "underline", + value: true + }) + }); + + fontColor = new formattingSettings.ColorPicker({ + name: "fontColor", + displayName: "Color", + value: { value: "#000000" } + }); + background = new formattingSettings.ColorPicker({ + name: "background", + displayName: "Background Color", + value: { value: "#FFFFFF" } + }); + slices = [this.textProperty, this.font, this.fontColor, this.background, this.position]; +} + +/** +* BarChart formatting settings model class +*/ +export class BarChartSettingsModel extends Model { + enableAxis = new EnableAxisCardSettings(); + colorSelector = new ColorSelectorCardSettings(); + generalView = new GeneralViewCardSettings(); + averageLine = new AverageLineCardSettings(); + directEditSettings = new DirectEditSettings(); + cards = [this.enableAxis, this.colorSelector, this.generalView, this.averageLine, this.directEditSettings]; + + /** + * populate colorSelector object categories formatting properties + * @param dataPoints + */ + populateColorSelector(dataPoints: BarChartDataPoint[]) { + const slices: formattingSettings.ColorPicker[] = this.colorSelector.slices; + if (dataPoints) { + dataPoints.forEach(dataPoint => { + slices.push(new formattingSettings.ColorPicker({ + name: "fill", + displayName: dataPoint.category, + value: { value: dataPoint.color }, + selector: dataPoint.selectionId.getSelector(), + })); + }); + } + } +} diff --git a/style/visual.less b/style/visual.less index fe05eb9..1cd34eb 100644 --- a/style/visual.less +++ b/style/visual.less @@ -1,10 +1,11 @@ p { font-size: 20px; font-weight: bold; + em { background: yellow; padding: 5px; - + } } @@ -45,4 +46,23 @@ p { top: 50px; font-size: 12pt; right: 15px; +} + +.direct-edit { + position: absolute; + bottom: 60%; + right: 12px; + display: inline-block; + width: 40%; + height: 20%; + border: 2px solid #80B0E0; + border-radius: 20px; + color: #80B0E0; + text-align: center; + font-size: 16px; + padding: 5px; +} + +.hidden { + display: none; } \ No newline at end of file