diff --git a/samples/widgets/README.md b/samples/widgets/README.md index 0b4cdddc..4028fcd4 100644 --- a/samples/widgets/README.md +++ b/samples/widgets/README.md @@ -1,18 +1,26 @@ ## Samples +* [Add layers](add-layers) +* [Client-side output data source](client-side-output) +* [Create a data source using a feature layer (class)](feature-layer-class) +* [Create a data source using a feature layer (function)](feature-layer-function) * [Creating a class component/widget](demo) * [Creating a functional component/widget](demo-function) * [Display a webmap as a data source](map-view) -* [Legend widget](js-api-widget) -* [Show extent with a map view](showextent) -* [Create a data source using a feature layer (class)](feature-layer-class) -* [Create a data source using a feature layer (function)](feature-layer-function) * [Editor widget](editor) * [Get map coordinates (class)](get-map-coordinates-class) * [Get map coordinates (function)](get-map-coordinates-function) +* [Legend widget](js-api-widget) +* [Message action](message-subscriber) +* [Point clustering](clustering) +* [Server-side output data source](server-side-output) +* [Share state between widgets](redux) +* [Show a record id](show-record-id) +* [Show extent with a map view](showextent) * [Using a D3 library](d3) * [Using a jQuery library](jquery) * [Using a react data grid library](react-data-grid) -* [Share state between widgets](redux) +* [Using statistics to create an output data source](statistic-client-side-output) +* [View layers toggle](view-layers-toggle) * [Web component](web-component) diff --git a/samples/widgets/add-layers/manifest.json b/samples/widgets/add-layers/manifest.json index 3f0ef68f..ea60618a 100644 --- a/samples/widgets/add-layers/manifest.json +++ b/samples/widgets/add-layers/manifest.json @@ -1,8 +1,8 @@ { "name": "add-layers", "type": "widget", - "version": "1.3.0", - "exbVersion": "1.3.0", + "version": "1.4.0", + "exbVersion": "1.4.0", "author": "Esri R&D Center Beijing", "description": "This widget demonstrates how to add a layer to a map programmatically.", "copyright": "", diff --git a/samples/widgets/client-side-output/README.md b/samples/widgets/client-side-output/README.md new file mode 100644 index 00000000..4a303543 --- /dev/null +++ b/samples/widgets/client-side-output/README.md @@ -0,0 +1,43 @@ +# Client-side output data source + +An output data source can save data in a data source instance, this is called a client-side output data source. + +## How to use the sample + +Clone the [sample repo](https://github.com/esri/arcgis-experience-builder-sdk-resources) and copy this widget's folder (within `samples/widgets`) to the `client/your-extensions/widgets` folder of your Experience Builder installation. + +## How it works + +In `setting.tsx`, use `DataSourceSelector` to allow the user to select an origin data source. Declare the output data source inside `onChange` callback of `DataSourceSelector`. + +```ts +// Let framework know which data source current widget is using and which data source current widget is the output. +this.props.onSettingChange({ + id: this.props.id, + useDataSources: useDataSources +}, outputDsJsons) +``` + +Select a filter and save it in widget config. + +```tsx + +``` + +In `widget.tsx`, use `DataSourceComponent` to create origin data source instance. Update the output data source's source records every time the user clicks the `update output data source` button. + +```tsx + +``` + +Please note this widget only generates an output data source. If you want to use the output data source, please add another widget (such as the List widget) and select the output data source for it. This sample widget does not listen to the origin data source change, you can use `onDataSourceInfoChange` callback of the `DataSourceComponent` to listen to the origin data source change. diff --git a/samples/widgets/client-side-output/config.json b/samples/widgets/client-side-output/config.json new file mode 100644 index 00000000..7a73a41b --- /dev/null +++ b/samples/widgets/client-side-output/config.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/samples/widgets/client-side-output/icon.svg b/samples/widgets/client-side-output/icon.svg new file mode 100644 index 00000000..c5d1ba73 --- /dev/null +++ b/samples/widgets/client-side-output/icon.svg @@ -0,0 +1,11 @@ + + + + widget-demo + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/samples/widgets/client-side-output/manifest.json b/samples/widgets/client-side-output/manifest.json new file mode 100644 index 00000000..31650577 --- /dev/null +++ b/samples/widgets/client-side-output/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "client-side-output", + "type": "widget", + "version": "1.4.0", + "exbVersion": "1.4.0", + "author": "Esri R&D Center Beijing", + "description": "This is the widget used in developer guide", + "copyright": "", + "license": "http://www.apache.org/licenses/LICENSE-2.0", + "defaultSize": { + "width": 400, + "height": 400 + } +} \ No newline at end of file diff --git a/samples/widgets/client-side-output/src/config.ts b/samples/widgets/client-side-output/src/config.ts new file mode 100644 index 00000000..fb8de69b --- /dev/null +++ b/samples/widgets/client-side-output/src/config.ts @@ -0,0 +1,7 @@ +import { ImmutableObject, IMSqlExpression } from 'jimu-core' + +export interface Config { + sqlExpression?: IMSqlExpression +} + +export type IMConfig = ImmutableObject diff --git a/samples/widgets/client-side-output/src/runtime/widget.tsx b/samples/widgets/client-side-output/src/runtime/widget.tsx new file mode 100644 index 00000000..1951769c --- /dev/null +++ b/samples/widgets/client-side-output/src/runtime/widget.tsx @@ -0,0 +1,105 @@ +import { React, AllWidgetProps, DataSourceComponent, dataSourceUtils, DataSourceManager, FeatureLayerDataSource, DataSourceStatus } from 'jimu-core' +import { Button } from 'jimu-ui' + +import { IMConfig } from '../config' + +export default class Widget extends React.PureComponent, unknown> { + isDsConfigured = () => { + if (this.props.useDataSources && this.props.useDataSources.length === 1) { + return true + } + return false + } + + onDataSourceCreated = () => { + this.setSourceRecordsToOutputDs() + } + + onCreateDataSourceFailed = () => { + this.setSourceRecordsToOutputDs() + } + + getOriginDataSource = () => { + return DataSourceManager.getInstance().getDataSource(this.props.useDataSources?.[0]?.dataSourceId) + } + + getOutputDataSource = () => { + return DataSourceManager.getInstance().getDataSource(this.props.outputDataSources?.[0]) + } + + setSourceRecordsToOutputDs = () => { + /** + * Just like using other data sources, to use an output data source, widget should use it through `DataSourceComponent`, the framework will create the data source instance on the fly. + * No output data source instance means there isn't any widgets using the output data source, + * in this case, no need to set source to the output data source. + */ + if (!this.getOutputDataSource()) { + return + } + + /** + * Need origin data source instance to get source records. + * If do not have origin data source instance, set status of output data source to not ready, which indicates output data source is not ready to do query. + */ + if (!this.getOriginDataSource()) { + this.getOutputDataSource().setStatus(DataSourceStatus.NotReady) + this.getOutputDataSource().setCountStatus(DataSourceStatus.NotReady) + return + } + + const sql = dataSourceUtils.getArcGISSQL(this.props.config.sqlExpression, this.getOriginDataSource()).sql || '1=1' + const featureLayerDs = this.getOriginDataSource() as FeatureLayerDataSource + featureLayerDs.query({ where: sql }).then(res => { + /** + * Set source to the output data source. + * Output data source can use the source to do query, to load records and so on. + * If use the source to load records, + * will save the loaded records to output data source instance and widget can get these records by `outputDs.getRecords()`. + */ + this.getOutputDataSource()?.setSourceRecords(res.records) + /** + * Status of output data source is not ready by default, set it to unloaded to let other widgets know output data source is ready to do query. + */ + this.getOutputDataSource()?.setStatus(DataSourceStatus.Unloaded) + this.getOutputDataSource()?.setCountStatus(DataSourceStatus.Unloaded) + }) + } + + render () { + if (!this.isDsConfigured()) { + return ( +

+ This widget demonstrates how to use attribute query results to generate a client-side output data source. +
+ Please config data source. +

+ ) + } + + return ( +
+

+ The client-side output data source is generated. +

+
+ If you want to use the client-side output data source, you should add another widget (such as List) and configure the output data source for it. +
+
+ If the widget's origin data source is changed (e.g. add a filter), the output data source won't update automatically. Please click the following button. +
+ + + + + +
+ ) + } +} diff --git a/samples/widgets/client-side-output/src/setting/setting.tsx b/samples/widgets/client-side-output/src/setting/setting.tsx new file mode 100644 index 00000000..3a0df1f8 --- /dev/null +++ b/samples/widgets/client-side-output/src/setting/setting.tsx @@ -0,0 +1,89 @@ +import { React, Immutable, DataSourceTypes, DataSourceManager, UseDataSource, DataSourceJson, SqlExpressionMode, IMSqlExpression } from 'jimu-core' +import { AllWidgetSettingProps } from 'jimu-for-builder' +import { SqlExpressionBuilderPopup } from 'jimu-ui/advanced/sql-expression-builder' +import { getJimuFieldNamesBySqlExpression } from 'jimu-ui/basic/sql-expression-runtime' +import { DataSourceSelector } from 'jimu-ui/advanced/data-source-selector' +import { IMConfig } from '../config' +import { Button } from 'jimu-ui' + +interface State { + isSqlBuilderOpen: boolean +} + +export default class Setting extends React.PureComponent, State> { + supportedTypes = Immutable([DataSourceTypes.FeatureLayer]) + + constructor (props) { + super(props) + this.state = { + isSqlBuilderOpen: false + } + } + + onDataSourceChange = (useDataSources: UseDataSource[]) => { + if (useDataSources?.length > 0) { + const originDsId = useDataSources[0]?.dataSourceId + const originDs = DataSourceManager.getInstance().getDataSource(originDsId) + const outputDsJsons: DataSourceJson[] = [{ + id: `${this.props.id}-ouput`, + type: DataSourceTypes.FeatureLayer, + label: `${this.props.manifest.name}-output-data-source`, + geometryType: originDs.getDataSourceJson().geometryType, + originDataSources: [useDataSources[0]], + isDataInDataSourceInstance: true + }] + + // Let framework know which data source current widget is using and which data source current widget is outputing. + this.props.onSettingChange({ + id: this.props.id, + useDataSources: useDataSources + }, outputDsJsons) + } + } + + onSqlExpressionChange = (sqlExpression: IMSqlExpression) => { + const usedFields = getJimuFieldNamesBySqlExpression(sqlExpression) + const newUseDs = this.props.useDataSources[0].set('fields', usedFields).asMutable({ deep: true }) + + // Save the SQL expression to config and update use data sources based on the fields SQL expression is using. + this.props.onSettingChange({ + id: this.props.id, + useDataSources: [newUseDs], + config: { sqlExpression } + }) + } + + toggleSqlBuilder = () => this.setState({ isSqlBuilderOpen: !this.state.isSqlBuilderOpen }) + + render () { + const dsId = this.props.useDataSources?.[0]?.dataSourceId + const ds = dsId && DataSourceManager.getInstance().getDataSource(dsId) + return ( +
+ + + { + ds &&
+ +
+ } + + +
+ ) + } +} diff --git a/samples/widgets/clustering/README.md b/samples/widgets/clustering/README.md new file mode 100755 index 00000000..7428c94d --- /dev/null +++ b/samples/widgets/clustering/README.md @@ -0,0 +1,97 @@ +# Point clustering + +This sample demonstrates how to enable point clustering on a feature layer in a web map. + +## How to use the sample +Clone the [sample repo](https://github.com/esri/arcgis-experience-builder-sdk-resources) and copy this widget's folder (within `samples/widgets`) to the `client/your-extensions/widgets` folder of your Experience Builder installation. + +## How it works +The property `clusterConfig` defines the cluster properties such as the `clusterRadius`, which determines each cluster's region for including features. + + ```javascript + clusterConfig = { + type: "cluster", + clusterRadius: "100px", + popupTemplate: { + content: "This cluster represents {cluster_count} points.", + fieldInfos: [{ + fieldName: "cluster_count", + format: { + digitSeparator: true, + places: 0 + } + }] + }, + clusterMinSize: "24px", + clusterMaxSize: "60px", + labelingInfo: [{ + deconflictionStrategy: "none", + labelExpressionInfo: { + expression: "Text($feature.cluster_count, '#,###')" + }, + + labelPlacement: "center-center", + }] + +} + +``` + +`onCheckBoxChange` function handles the logic for the `Checkbox` component and sets the state based on the value and passes this to the `clusterSwitch` function. +```javascript + onCheckBoxChange = (e) => { + const valueState = e.target.value + this.setState({ + clusterStatus: valueState + }, () => { + this.clusterSwitch(this.state.clusterStatus); + }) +} + +``` + +The `clusterSwitch` function controls the display of the cluster. If `this.state.clusterStatus` is true, then enable clustering. Otherwise, turn off the clustering for the layer by setting it to `null`. +```javascript +clusterSwitch = (e) => { + if (this.state.clusterStatus){ + this.state.jimuMapView.jimuLayerViews[this.state.clusterLayer].layer.featureReduction = this.clusterConfig + } + if (!this.state.clusterStatus) + + this.state.jimuMapView.jimuLayerViews[this.state.clusterLayer].layer.featureReduction = this.state.clusterStatus ? this.clusterConfig : null + +} + +``` + +The `activeViewChangeHandler` function handles a couple of things. It checks if there is a `JimuMapView` and if it contains layers. If it does contain layers then it will set the state for map view, the data source and the error tip to display the widget. If there is a `JimuMapView` without layers, then it will display the `WidgetPlaceHolder` component. + + ```javascript + activeViewChangeHandler = (jmv: JimuMapView) => { + if (jmv) { + const jimuLayerViews = jmv.jimuLayerViews; + const jimuLayerViewIds = Object.keys(jimuLayerViews)[0]; + const layerViewId = jimuLayerViewIds; + + if (layerViewId === undefined) { + jmv = null; + return + } + + this.setState({ + jimuMapView: jmv, + clusterLayer: jimuLayerViewIds, + errorTip: false + }) + } else { + this.setState({ + jimuMapView: undefined, + clusterLayer: undefined, + errorTip: true, + clusterStatus: false + }) + + } + } + +``` diff --git a/samples/widgets/clustering/config.json b/samples/widgets/clustering/config.json new file mode 100755 index 00000000..0967ef42 --- /dev/null +++ b/samples/widgets/clustering/config.json @@ -0,0 +1 @@ +{} diff --git a/samples/widgets/clustering/icon.svg b/samples/widgets/clustering/icon.svg new file mode 100755 index 00000000..57509e14 --- /dev/null +++ b/samples/widgets/clustering/icon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/widgets/clustering/manifest.json b/samples/widgets/clustering/manifest.json new file mode 100755 index 00000000..d032a0c6 --- /dev/null +++ b/samples/widgets/clustering/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "clustering", + "type": "widget", + "version": "1.4.0", + "exbVersion": "1.4.0", + "author": "Esri R&D Center Beijing", + "description": "This is the widget used in developer documentation", + "copyright": "", + "dependency": "jimu-arcgis", + "license": "http://www.apache.org/licenses/LICENSE-2.0", + "properties": {}, + "translatedLocales": [ + "en" + ], + "defaultSize": { + "width": 247, + "height": 94 + } +} \ No newline at end of file diff --git a/samples/widgets/clustering/src/runtime/assets/cluster.svg b/samples/widgets/clustering/src/runtime/assets/cluster.svg new file mode 100755 index 00000000..c07cd5f3 --- /dev/null +++ b/samples/widgets/clustering/src/runtime/assets/cluster.svg @@ -0,0 +1,9 @@ + + +cluster + + + + + + diff --git a/samples/widgets/clustering/src/runtime/translations/default.ts b/samples/widgets/clustering/src/runtime/translations/default.ts new file mode 100755 index 00000000..b7796f68 --- /dev/null +++ b/samples/widgets/clustering/src/runtime/translations/default.ts @@ -0,0 +1,22 @@ +/** + Licensing + + Copyright 2021 Esri + + Licensed under the Apache License, Version 2.0 (the "License"); You + may not use this file except in compliance with the License. You may + obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + A copy of the license is available in the repository's + LICENSE file. +*/ +export default { + _widgetLabel: "Clustering" +}; \ No newline at end of file diff --git a/samples/widgets/clustering/src/runtime/widget.tsx b/samples/widgets/clustering/src/runtime/widget.tsx new file mode 100755 index 00000000..186dfe52 --- /dev/null +++ b/samples/widgets/clustering/src/runtime/widget.tsx @@ -0,0 +1,144 @@ +/** @jsx jsx */ +/** + Licensing + + Copyright 2021 Esri + + Licensed under the Apache License, Version 2.0 (the "License"); You + may not use this file except in compliance with the License. You may + obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + A copy of the license is available in the repository's + LICENSE file. +*/ +import { React, AllWidgetProps, jsx } from 'jimu-core'; +import { JimuMapViewComponent, JimuMapView } from "jimu-arcgis"; +import { Label, Checkbox, WidgetPlaceholder } from 'jimu-ui'; + +interface State { + jimuMapView: JimuMapView; + clusterLayer: string; + clusterStatus: boolean; + errorTip: boolean; +} + +export default class Widget extends React.PureComponent, State> { + + constructor(props) { + super(props); + + this.state = { + jimuMapView: undefined, + clusterLayer: undefined, + clusterStatus: false, + errorTip: true + }; + } + + clusterConfig = { + type: "cluster", + clusterRadius: "100px", + popupTemplate: { + content: "This cluster represents {cluster_count} points.", + fieldInfos: [{ + fieldName: "cluster_count", + format: { + digitSeparator: true, + places: 0 + } + }] + }, + clusterMinSize: "24px", + clusterMaxSize: "60px", + labelingInfo: [{ + deconflictionStrategy: "none", + labelExpressionInfo: { + expression: "Text($feature.cluster_count, '#,###')" + }, + + labelPlacement: "center-center", + }] + + } + + onCheckBoxChange = (e) => { + const valueState = e.target.value + this.setState({ + clusterStatus: valueState + }, () => { + this.clusterSwitch(this.state.clusterStatus); + }) + } + + clusterSwitch = (e) => { + if (this.state.clusterStatus) { + this.state.jimuMapView.jimuLayerViews[this.state.clusterLayer].layer.featureReduction = this.clusterConfig + } + if (!this.state.clusterStatus) + + this.state.jimuMapView.jimuLayerViews[this.state.clusterLayer].layer.featureReduction = this.state.clusterStatus ? this.clusterConfig : null + + } + + activeViewChangeHandler = (jmv: JimuMapView) => { + if (jmv) { + const jimuLayerViews = jmv.jimuLayerViews; + const jimuLayerViewIds = Object.keys(jimuLayerViews)[0]; + const layerViewId = jimuLayerViewIds; + + if (layerViewId === undefined) { + jmv = null; + return + } + + this.setState({ + jimuMapView: jmv, + clusterLayer: jimuLayerViewIds, + errorTip: false + }) + } else { + this.setState({ + jimuMapView: undefined, + clusterLayer: undefined, + errorTip: true, + clusterStatus: false + }) + + } + } + + renderWidgetPlaceholder() { + return ; + } + render() { + const mapContent = + let clusterContent = null; + if (this.state.errorTip || !(this.props.useMapWidgetIds && this.props.useMapWidgetIds.length > 0)) { + clusterContent = this.renderWidgetPlaceholder(); + } else { + clusterContent = + + } + return
+ {clusterContent} +
{mapContent}
+
+ } +} diff --git a/samples/widgets/clustering/src/setting/setting.tsx b/samples/widgets/clustering/src/setting/setting.tsx new file mode 100755 index 00000000..567c9086 --- /dev/null +++ b/samples/widgets/clustering/src/setting/setting.tsx @@ -0,0 +1,50 @@ +/** + Licensing + + Copyright 2021 Esri + + Licensed under the Apache License, Version 2.0 (the "License"); You + may not use this file except in compliance with the License. You may + obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + A copy of the license is available in the repository's + LICENSE file. +*/ +import {React} from 'jimu-core'; +import {AllWidgetSettingProps} from 'jimu-for-builder'; +import { JimuMapViewSelector, SettingRow, SettingSection} from "jimu-ui/advanced/setting-components"; + +export default class Setting extends React.PureComponent, {}> { + + onMapSelected = (useMapWidgetIds: string[]) => { + this.props.onSettingChange({ + id: this.props.id, + useMapWidgetIds: useMapWidgetIds, + }); + }; + + render(){ + return
+ + + + + +
+ } +} \ No newline at end of file diff --git a/samples/widgets/d3/manifest.json b/samples/widgets/d3/manifest.json index 0724c86c..174ce466 100644 --- a/samples/widgets/d3/manifest.json +++ b/samples/widgets/d3/manifest.json @@ -1,8 +1,8 @@ { "name": "d3", "type": "widget", - "version": "1.3.0", - "exbVersion": "1.3.0", + "version": "1.4.0", + "exbVersion": "1.4.0", "author": "Esri R&D Center Beijing", "description": "This widget demonstrates how to use D3.js.", "copyright": "", diff --git a/samples/widgets/d3/src/runtime/widget.tsx b/samples/widgets/d3/src/runtime/widget.tsx index 3f528b86..f93d2913 100644 --- a/samples/widgets/d3/src/runtime/widget.tsx +++ b/samples/widgets/d3/src/runtime/widget.tsx @@ -18,80 +18,78 @@ A copy of the license is available in the repository's LICENSE file. */ -import { React, AllWidgetProps, jsx } from "jimu-core"; +import { React, jsx } from "jimu-core"; +const { useEffect, useRef } = React; import * as d3 from "./lib/d3/d3.min.js"; -export default class Widget extends React.PureComponent, any> { +export default function Widget() { + // Create a React Ref - https://reactjs.org/docs/refs-and-the-dom.html - private mainRef = React.createRef(); - - constructor(props) { - super(props); - this.state = {}; - } - - componentDidMount() { - /** - * START code from - * https://observablehq.com/@d3/lets-make-a-bar-chart - */ - const data = [4, 8, 15, 16, 23, 42]; - - const x = d3 - .scaleLinear() - .domain([0, d3.max(data)]) - .range([0, 100]); - - const div = d3.create("div"); - // Apply some styles to the chart container. - div.style("font", "10px sans-serif"); - div.style("text-align", "right"); - div.style("color", "white"); - - // Define the initial (empty) selection for the bars. - const bar = div.selectAll("div"); - - // Bind this selection to the data (computing enter, update and exit). - const barUpdate = bar.data(data); - - // Join the selection and the data, appending the entering bars. - const barNew = barUpdate.join("div"); - - // Apply some styles to the bars. - barNew.style("background", "steelblue"); - barNew.style("padding", "3px"); - barNew.style("margin", "1px"); - - // Set the width as a function of data. - // barNew.style("width", (d) => `${d * 10}px`); - barNew.style("width", (d) => `${x(d)}px`); - - // Set the text of each bar as the data. - barNew.text((d) => d); - /** - * END code from - * https://observablehq.com/@d3/lets-make-a-bar-chart - */ - - // Please the results from the D3 operation into the DOM using - // react references - https://reactjs.org/docs/refs-and-the-dom.html - this.mainRef.current.appendChild(div.node()); - } - - render() { - return ( -
-

- This widget shows how to include an external library (D3.js) in your - custom Experience Builder widget. The D3.js code being used comes from + const mainRef = useRef(); + + useEffect(() => { + if (mainRef && mainRef.current) { + /** + * START code from + * https://observablehq.com/@d3/lets-make-a-bar-chart + */ + const data = [4, 8, 15, 16, 23, 42]; + + const x = d3 + .scaleLinear() + .domain([0, d3.max(data)]) + .range([0, 100]); + + const div = d3.create("div"); + // Apply some styles to the chart container. + div.style("font", "10px sans-serif"); + div.style("text-align", "right"); + div.style("color", "white"); + + // Define the initial (empty) selection for the bars. + const bar = div.selectAll("div"); + + // Bind this selection to the data (computing enter, update and exit). + const barUpdate = bar.data(data); + + // Join the selection and the data, appending the entering bars. + const barNew = barUpdate.join("div"); + + // Apply some styles to the bars. + barNew.style("background", "steelblue"); + barNew.style("padding", "3px"); + barNew.style("margin", "1px"); + + // Set the width as a function of data. + // barNew.style("width", (d) => `${d * 10}px`); + barNew.style("width", (d) => `${x(d)}px`); + + // Set the text of each bar as the data. + barNew.text((d) => d); + /** + * END code from + * https://observablehq.com/@d3/lets-make-a-bar-chart + */ + + // Place the results from the D3 operation into the DOM using + // react references - https://reactjs.org/docs/refs-and-the-dom.html + mainRef.current.appendChild(div.node()); + } + + }, [mainRef]); + + return ( +

+

+ This widget shows how to include an external library (D3.js) in your + custom Experience Builder widget. The D3.js code being used comes from the tutorial{" "} - - Let’s Make a Bar Chart, Part 1 + + Let’s Make a Bar Chart, Part 1 .

-
-
- ); - } +
+
+ ); } diff --git a/samples/widgets/demo-function/manifest.json b/samples/widgets/demo-function/manifest.json index 042df156..100f7edb 100644 --- a/samples/widgets/demo-function/manifest.json +++ b/samples/widgets/demo-function/manifest.json @@ -2,8 +2,8 @@ "name": "demo-function", "label": "Demo Function", "type": "widget", - "version": "1.3.0", - "exbVersion": "1.3.0", + "version": "1.4.0", + "exbVersion": "1.4.0", "author": "Esri R&D Center Beijing", "description": "This is the widget used in developer documentation", "copyright": "", diff --git a/samples/widgets/demo-function/src/runtime/widget.tsx b/samples/widgets/demo-function/src/runtime/widget.tsx index 1b7eb872..da38d0a7 100644 --- a/samples/widgets/demo-function/src/runtime/widget.tsx +++ b/samples/widgets/demo-function/src/runtime/widget.tsx @@ -17,16 +17,16 @@ A copy of the license is available in the repository's LICENSE file. */ -import {AllWidgetProps, React, IMState, FormattedMessage} from 'jimu-core'; -import {IMConfig} from '../config'; +import { AllWidgetProps, React, IMState, FormattedMessage } from 'jimu-core'; +import { IMConfig } from '../config'; import defaultMessage from './translations/default'; -interface ExtraProps{ +interface ExtraProps { locale: string; } -export default function Widget(props: AllWidgetProps & ExtraProps){ - return
+export default function Widget(props: AllWidgetProps & ExtraProps) { + return
This widget demonstrates creating a functional component.
config: {JSON.stringify(props.config)} diff --git a/samples/widgets/demo/README.md b/samples/widgets/demo/README.md index 3f074c16..07cb0469 100644 --- a/samples/widgets/demo/README.md +++ b/samples/widgets/demo/README.md @@ -1,4 +1,4 @@ -# Demo widget +# Demo class widget This sample demonstrates how to create a widget using a class component. ## How to use the sample diff --git a/samples/widgets/demo/manifest.json b/samples/widgets/demo/manifest.json index 6b8d79b2..f8023844 100644 --- a/samples/widgets/demo/manifest.json +++ b/samples/widgets/demo/manifest.json @@ -2,8 +2,8 @@ "name": "demo", "label": "Demo", "type": "widget", - "version": "1.3.0", - "exbVersion": "1.3.0", + "version": "1.4.0", + "exbVersion": "1.4.0", "author": "Esri R&D Center Beijing", "description": "This is the widget used in developer documentation", "copyright": "", diff --git a/samples/widgets/editor/README.md b/samples/widgets/editor/README.md index 63f241aa..9f74d39a 100644 --- a/samples/widgets/editor/README.md +++ b/samples/widgets/editor/README.md @@ -9,7 +9,7 @@ Clone the [sample repo](https://github.com/esri/arcgis-experience-builder-sdk-re This sample imports the required modules to leverage the `Editor` widget from the ArcGIS API for JavaScript. The `Editor` widget is dependent on a `Map` widget, which must have an editable feature layer. If it recognizes it is editable, the layer can be used by the widget. ```javascript - import Editor = require('esri/widgets/Editor'); + import * as Editor from 'esri/widgets/Editor'; ``` diff --git a/samples/widgets/editor/manifest.json b/samples/widgets/editor/manifest.json index c4fbdfce..47959f63 100644 --- a/samples/widgets/editor/manifest.json +++ b/samples/widgets/editor/manifest.json @@ -1,9 +1,9 @@ { "name": "editor", "type": "widget", - "version": "1.3.0", + "version": "1.4.0", "dependency": "jimu-arcgis", - "exbVersion": "1.3.0", + "exbVersion": "1.4.0", "author": "Esri R&D Center Beijing", "description": "This is the widget used in developer guide", "copyright": "", diff --git a/samples/widgets/editor/src/runtime/widget.tsx b/samples/widgets/editor/src/runtime/widget.tsx index 82f73c60..530d615b 100644 --- a/samples/widgets/editor/src/runtime/widget.tsx +++ b/samples/widgets/editor/src/runtime/widget.tsx @@ -18,9 +18,9 @@ LICENSE file. */ /** @jsx jsx */ -import { AllWidgetProps, jsx, React, css } from "jimu-core"; +import { AllWidgetProps, BaseWidget, jsx, React } from "jimu-core"; import { JimuMapViewComponent, JimuMapView } from "jimu-arcgis"; -import Editor = require('esri/widgets/Editor'); +import * as Editor from 'esri/widgets/Editor' interface State { @@ -80,8 +80,7 @@ export default class Widget extends React.PureComponent, Stat render() { let mvc =

Please select a map.

; - - + const css = ` .esri-editor__scroller { overflow-y: auto; @@ -92,15 +91,16 @@ export default class Widget extends React.PureComponent, Stat .esri-editor__content-group { max-height: 1em; } + ` if ( this.props.hasOwnProperty("useMapWidgetIds") && this.props.useMapWidgetIds && - this.props.useMapWidgetIds.length === 1 + this.props.useMapWidgetIds[0] ) { mvc = ( ); @@ -108,8 +108,8 @@ export default class Widget extends React.PureComponent, Stat return (