├── .gitignore
├── README.md
├── browser
├── index.html
└── src
│ └── app.ts
├── package-lock.json
├── package.json
├── patterns
├── AccessibleNodes.tsx
├── Binding.tsx
├── BooleanAttributes.tsx
├── CleaningUp.tsx
├── CustomEventHandlerArguments.tsx
├── DealingWithWhitespace.tsx
├── DefiningGettersAndSetters.tsx
├── DistinguishableChildren.tsx
├── HandlingChildrenEvents.tsx
├── HandlingMultipleEvents.tsx
├── InnerHTML.tsx
├── ManagingListItemFocus.tsx
├── NodeCreation.tsx
├── ReferencingNodesOnDemand.tsx
├── RenderingArrayItems.tsx
├── RenderingCollectionItems.tsx
├── RoleAttribute.tsx
├── SchedulingRender.tsx
├── SpreadingAttributes.tsx
├── SyntheticEvents.tsx
├── TogglingNodeVisibility.tsx
├── UsingDgrid.tsx
├── UsingDijit.tsx
├── WorkingWithSVG.tsx
├── manifest.json
└── support
│ └── loadStylesheet.ts
├── tsconfig.json
└── typings
├── arcgis-js-api-4.7.d.ts
└── arcgis-js-api-extensions.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | node_modules/
4 | *.js
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 4x Widget Resources
2 |
3 | Collection of focused examples useful for widget development.
4 |
5 | ## Interactive Browser
6 |
7 | https://jcfranco.github.io/4x-widget-patterns/browser
8 |
9 | ## Requirements
10 |
11 | * [node & npm](https://nodejs.org/)
12 |
13 | ## Usage
14 |
15 | * `npm install` - installs required node and bower packages
16 | * Open a browser and go to `examples/index.html` (assumes repository is web-accessible)
17 |
18 | ## Notes
19 |
20 | * IIS (Windows) requires adding `*.tsx` as a MIME type for examples to load correctly.
21 |
--------------------------------------------------------------------------------
/browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Widget 4.x widget patterns
7 |
8 |
9 |
10 |
32 |
48 |
49 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Pattern
59 |
60 |
61 |
67 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/browser/src/app.ts:
--------------------------------------------------------------------------------
1 | import manifest = require("dojo/text!patterns/manifest.json");
2 |
3 | window.addEventListener("hashchange", function() {
4 | window.location.reload(true);
5 | });
6 |
7 | function byId(id: string): HTMLElement {
8 | return document.getElementById(id);
9 | }
10 |
11 | function toLabel(pattern: string): string {
12 | return pattern.replace(/([a-z])([A-Z])/g, "$1 $2");
13 | }
14 |
15 | interface Manifest {
16 | patterns: string[];
17 | }
18 |
19 | const patternToken = decodeURI(location.hash).slice(1);
20 | const patternName = patternToken.replace(/-/g, ""); // strip delimiters
21 | const { patterns } = JSON.parse(manifest) as Manifest;
22 |
23 | const select = byId("pattern-select") as HTMLSelectElement;
24 |
25 | function redirectToOption(optionIndex: number): void {
26 | window.location.hash = select.options[optionIndex].value
27 | }
28 |
29 | patterns.forEach(pattern => {
30 | const option = document.createElement("option");
31 | option.label = toLabel(pattern);
32 | option.value = pattern;
33 | option.selected = pattern.toLowerCase() === patternName.toLowerCase();
34 | select.appendChild(option);
35 | });
36 |
37 | select.addEventListener("input", function() {
38 | redirectToOption(select.selectedIndex);
39 | });
40 |
41 | if (!patternName) {
42 | redirectToOption(0);
43 | }
44 |
45 | document.title = `${document.title} - ${patternToken}`;
46 |
47 | const patternMid = `patterns/${patternName}`;
48 | const patternCodeMid = `dojo/text!${patternMid}.tsx`;
49 |
50 | require([
51 | patternMid,
52 | patternCodeMid,
53 | "highlight"
54 | ], (Pattern: any, patternCode: string, highlight: any) => {
55 | const codeDiv = byId("codeDiv");
56 | codeDiv.textContent = patternCode;
57 | highlight.highlightBlock(codeDiv);
58 |
59 | new Pattern({ container: "widgetDiv" });
60 | });
61 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4x-widget-snippets",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/chai": {
8 | "version": "4.1.2",
9 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.2.tgz",
10 | "integrity": "sha512-D8uQwKYUw2KESkorZ27ykzXgvkDJYXVEihGklgfp5I4HUP8D6IxtcdLTMB1emjQiWzV7WZ5ihm1cxIzVwjoleQ==",
11 | "dev": true
12 | },
13 | "@types/highlight.js": {
14 | "version": "9.12.2",
15 | "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.2.tgz",
16 | "integrity": "sha512-y5x0XD/WXDaGSyiTaTcKS4FurULJtSiYbGTeQd0m2LYZGBcZZ/7fM6t5H/DzeUF+kv8y6UfmF6yJABQsHcp9VQ==",
17 | "dev": true
18 | },
19 | "dojo-typings": {
20 | "version": "1.11.9",
21 | "resolved": "https://registry.npmjs.org/dojo-typings/-/dojo-typings-1.11.9.tgz",
22 | "integrity": "sha512-mh8w+Mau2Y1QfTEszEAdO7j6ycNhYxF/Ing6nAk1eUg6NxjeT0viVHjICMd9sU3U463vM2G+KfBBK5grk3/Mlw==",
23 | "dev": true,
24 | "requires": {
25 | "@types/chai": "4.1.2"
26 | }
27 | },
28 | "typescript": {
29 | "version": "2.7.2",
30 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz",
31 | "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==",
32 | "dev": true
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4x-widget-snippets",
3 | "version": "1.0.0",
4 | "description": "4x widget snippets",
5 | "scripts": {
6 | "postinstall": "tsc"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/jcfranco/4x-widget-snippets"
11 | },
12 | "keywords": [
13 | "jsapi",
14 | "widget",
15 | "resources"
16 | ],
17 | "author": "JC Franco",
18 | "license": "MIT",
19 | "devDependencies": {
20 | "@types/highlight.js": "~9.12.2",
21 | "dojo-typings": "~1.11.9",
22 | "typescript": "~2.7.2"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/patterns/AccessibleNodes.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {renderable, tsx, accessibleHandler} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-accessible-nodes"
12 | };
13 |
14 | @subclass("examples.AccessibleNodes")
15 | class AccessibleNodes extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Properties
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | //----------------------------------
24 | // enabled
25 | //----------------------------------
26 |
27 | @property()
28 | @renderable()
29 | enabled = true;
30 |
31 | //--------------------------------------------------------------------------
32 | //
33 | // Public Methods
34 | //
35 | //--------------------------------------------------------------------------
36 |
37 | render() {
38 | const child = this.enabled ? (
39 | ( ^∇^)ノ {'{'} ohai!
40 | ) : null;
41 |
42 | return (
43 |
48 | Click or press space/enter key (when focused) to toggle
49 | {child}
50 |
51 | );
52 | }
53 |
54 | //--------------------------------------------------------------------------
55 | //
56 | // Private Methods
57 | //
58 | //--------------------------------------------------------------------------
59 |
60 | @accessibleHandler()
61 | private _handleClick(): void {
62 | this.enabled = !this.enabled;
63 | }
64 |
65 | }
66 |
67 | export = AccessibleNodes;
68 |
--------------------------------------------------------------------------------
/patterns/Binding.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-binding"
12 | };
13 |
14 | @subclass("examples.Binding")
15 | class Binding extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Public Methods
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | render() {
24 | return (
25 |
26 |
this === widget
28 |
this === node
29 |
30 | );
31 | }
32 |
33 | //--------------------------------------------------------------------------
34 | //
35 | // Private Methods
36 | //
37 | //--------------------------------------------------------------------------
38 |
39 | private _handlerWhereThisIsWidget(): void {
40 | console.log(this); // this === widget instance
41 | }
42 |
43 | private _handlerWithThisIsNode(): void {
44 | console.log(this); // this === DOM node
45 | }
46 |
47 | }
48 |
49 | export = Binding;
50 |
--------------------------------------------------------------------------------
/patterns/BooleanAttributes.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-boolean-attributes"
12 | };
13 |
14 | @subclass("examples.BooleanAttributes")
15 | class BooleanAttributes extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Lifecycle
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | postInitialize() {
24 | setInterval(() => {
25 | this._selectedIndex = Math.floor(Math.random() * 10);
26 | this.scheduleRender();
27 | }, 500);
28 | }
29 |
30 | //--------------------------------------------------------------------------
31 | //
32 | // Variables
33 | //
34 | //--------------------------------------------------------------------------
35 |
36 | _selectedIndex = 0;
37 |
38 | //--------------------------------------------------------------------------
39 | //
40 | // Public Methods
41 | //
42 | //--------------------------------------------------------------------------
43 |
44 | render() {
45 | return (
46 |
47 | {/* boolean attributes require setting their value to an expression */}
48 |
49 | 0
50 | 1
51 | 2
52 | 3
53 | 4
54 | 5
55 | 6
56 | 7
57 | 8
58 | 9
59 | 10
60 |
61 |
62 | );
63 | }
64 |
65 | }
66 |
67 | export = BooleanAttributes;
68 |
--------------------------------------------------------------------------------
/patterns/CleaningUp.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import promiseUtils = require("esri/core/promiseUtils");
7 |
8 | import Widget = require("esri/widgets/Widget");
9 |
10 |
11 | import {renderable, tsx} from "esri/widgets/support/widget";
12 |
13 | const CSS = {
14 | base: "esri-cleaning-up"
15 | };
16 |
17 | @subclass("examples.CleaningUp")
18 | class CleaningUp extends declared(Widget) {
19 |
20 | //--------------------------------------------------------------------------
21 | //
22 | // Lifecycle
23 | //
24 | //--------------------------------------------------------------------------
25 |
26 | postInitialize() {
27 | const dayInSeconds = 86400;
28 |
29 | const dayElapsedPromise = promiseUtils.timeout(
30 | promiseUtils.resolve(), dayInSeconds, null
31 | );
32 |
33 | const counterWatcher = this.watch("counter", (value: number, oldValue: number, name: string) => {
34 | console.log(`${name} changed from ${oldValue} to ${value}`);
35 | });
36 |
37 | const counterIntervalId = setInterval(() => {
38 | console.log(`counter: ${this.counter++}`);
39 | }, 500);
40 |
41 | const nonHandleRemover = {
42 | remove() {
43 | clearInterval(counterIntervalId);
44 | dayElapsedPromise.cancel();
45 | }
46 | };
47 |
48 | // 'owned' handles will be destroyed automatically
49 | this.own([
50 | nonHandleRemover,
51 | counterWatcher
52 | ]);
53 | }
54 |
55 | //--------------------------------------------------------------------------
56 | //
57 | // Properties
58 | //
59 | //--------------------------------------------------------------------------
60 |
61 | //----------------------------------
62 | // counter
63 | //----------------------------------
64 |
65 | @property()
66 | @renderable()
67 | counter: number = 0;
68 |
69 | //--------------------------------------------------------------------------
70 | //
71 | // Public Methods
72 | //
73 | //--------------------------------------------------------------------------
74 |
75 | render() {
76 | return (
77 | {this.counter}
78 | );
79 | }
80 |
81 | }
82 |
83 | export = CleaningUp;
84 |
--------------------------------------------------------------------------------
/patterns/CustomEventHandlerArguments.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-custom-event-handler-arguments"
12 | };
13 |
14 | function numberBetween0And100() {
15 | return Math.floor((Math.random() * 100) + 1);
16 | }
17 |
18 | function luckyNumbers() {
19 | const nums = [
20 | numberBetween0And100(),
21 | numberBetween0And100(),
22 | numberBetween0And100(),
23 | numberBetween0And100(),
24 | numberBetween0And100(),
25 | numberBetween0And100(),
26 | numberBetween0And100()
27 | ];
28 |
29 | return `${nums[0]}-${nums[1]}-${nums[2]}-${nums[3]}-${nums[4]}-${nums[5]}-${nums[6]}`;
30 | }
31 |
32 | @subclass("examples.CustomEventHandlerArguments")
33 | class CustomEventHandlerArguments extends declared(Widget) {
34 |
35 | //--------------------------------------------------------------------------
36 | //
37 | // Public Methods
38 | //
39 | //--------------------------------------------------------------------------
40 |
41 | render() {
42 | return (
43 |
46 | Click me for your lucky numbers!
47 |
48 | );
49 | }
50 |
51 | //--------------------------------------------------------------------------
52 | //
53 | // Private Methods
54 | //
55 | //--------------------------------------------------------------------------
56 |
57 | private _handleClick(event: MouseEvent) {
58 | const node = event.currentTarget as Element;
59 | const luckyNums = node.getAttribute("data-lucky-numbers");
60 | console.log(`Today's lucky numbers are: ${luckyNums}`);
61 | }
62 |
63 | }
64 |
65 | export = CustomEventHandlerArguments;
66 |
--------------------------------------------------------------------------------
/patterns/DealingWithWhitespace.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | @subclass("examples.DealingWithWhitespace")
11 | class DealingWithWhitespace extends declared(Widget) {
12 |
13 | //--------------------------------------------------------------------------
14 | //
15 | // Public Methods
16 | //
17 | //--------------------------------------------------------------------------
18 |
19 | render() {
20 |
21 | // taken from http://www.bennadel.com/blog/2880-a-quick-look-at-rendering-white-space-using-jsx-in-reactjs.htm
22 | return (
23 |
24 |
25 |
26 | {
27 | // This content will render with no visible white space
28 | // since each sibling Element node that is defined on a
29 | // different line of CODE will be rendered right up
30 | // against the other ones.
31 | }
32 | Bro!
33 | You
34 | had
35 | me
36 | at
37 | "White space."
38 |
39 |
40 |
41 | {
42 | // This content will render with "expected" white space
43 | // since the Element node is on the same line with its
44 | // other content. As such, the white space around the
45 | // element is preserved.
46 | }
47 | There's literally nothing more exciting!
48 |
49 |
50 |
51 | {
52 | // In this case, we're going to lose the white space to
53 | // the LEFT of the first STRONG Element node and the RIGHT
54 | // of the second one. The plain-text lines are implicitly
55 | // wrapped in Span tags, which creates a multi-line sibling
56 | // relationship between Element nodes, which removes white
57 | // space (as we saw in the first example).
58 | }
59 | But don't get me started on
60 | kerning and tracking
61 | or we'll be here all day.
62 |
63 |
64 |
65 | {
66 | // This will render as expected since there are no
67 | // Element nodes. This entire value gets rendered as a
68 | // single value, NO implicit SPAN container.
69 | }
70 | That said, if you want to get groovy on the use of
71 | negative space in design, then I will be more than
72 | happy to chat. I have nowhere else to be at this moment.
73 |
74 |
75 |
76 | {
77 | // We can always force white space by interpolating a
78 | // white space literal. This way, when the JSX is
79 | // compiled down into React Element children, the white
80 | // space literal will be an explicit child.
81 | }
82 | Bro!
83 | { " " }
84 | You
85 | { " " }
86 | had
87 | { " " }
88 | me
89 | { " " }
90 | at
91 | { " " }
92 | "White space."
93 |
94 |
95 |
96 | );
97 |
98 | }
99 |
100 | }
101 |
102 | export = DealingWithWhitespace;
103 |
--------------------------------------------------------------------------------
/patterns/DefiningGettersAndSetters.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-defining-getters-and-setters"
12 | };
13 |
14 | @subclass("examples.DefiningGettersAndSetters")
15 | class DefiningGettersAndSetters extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Properties
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | //----------------------------------
24 | // normalProperty
25 | //----------------------------------
26 |
27 | @property()
28 | normalProperty = "normal";
29 |
30 | //----------------------------------
31 | // getterSetterProperty
32 | //----------------------------------
33 |
34 | @property({
35 | value: "getter-setter"
36 | })
37 | get getterSetterProperty(): boolean {
38 | return this._get("getterSetterProperty");
39 | }
40 |
41 | set getterSetterProperty(value: boolean) {
42 | this._set("getterSetterProperty", value);
43 | }
44 |
45 | //----------------------------------
46 | // readOnlyProperty
47 | //----------------------------------
48 |
49 | @property({
50 | value: "read-only",
51 | readOnly: true
52 | })
53 | get readOnlyProperty(): boolean {
54 | return this._get("readOnlyProperty");
55 | }
56 |
57 | //--------------------------------------------------------------------------
58 | //
59 | // Public Methods
60 | //
61 | //--------------------------------------------------------------------------
62 |
63 | render() {
64 | return (
65 |
66 |
{this.normalProperty}
67 |
{this.getterSetterProperty}
68 |
{this.readOnlyProperty}
69 |
70 | );
71 | }
72 |
73 | }
74 |
75 | export = DefiningGettersAndSetters;
76 |
--------------------------------------------------------------------------------
/patterns/DistinguishableChildren.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-distinguishable-children",
12 | item: "esri-distinguishable-children__item"
13 | };
14 |
15 | function createRandomListItems(): any[] {
16 | const children: any[] = [];
17 |
18 | for (let i = 0; i < randomNum(); i++) {
19 | children.push(
20 | // use `key` to distinguish this elements that will be added/removed dynamically
21 | item {i}
22 | );
23 | }
24 |
25 | return children;
26 | }
27 |
28 | function randomNum(max = 10): number {
29 | return Math.random() * max;
30 | }
31 |
32 | @subclass("examples.DistinguishableChildren")
33 | class DistinguishableChildren extends declared(Widget) {
34 |
35 | //--------------------------------------------------------------------------
36 | //
37 | // Lifecycle
38 | //
39 | //--------------------------------------------------------------------------
40 |
41 | postInitialize() {
42 | setInterval(() => this.scheduleRender(), 500);
43 | }
44 |
45 | //--------------------------------------------------------------------------
46 | //
47 | // Public Methods
48 | //
49 | //--------------------------------------------------------------------------
50 |
51 | render() {
52 | return (
53 | {
54 |
{createRandomListItems()}
55 | }
56 | );
57 | }
58 |
59 | }
60 |
61 | export = DistinguishableChildren;
62 |
--------------------------------------------------------------------------------
/patterns/HandlingChildrenEvents.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import Widget = require("esri/widgets/Widget");
5 |
6 | import { subclass, declared } from "esri/core/accessorSupport/decorators";
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-handling-children-events",
12 | item: "esri-handling-children-events__item"
13 | };
14 |
15 | @subclass("examples.HandlingChildrenEvents")
16 | class HandlingChildrenEvents extends declared(Widget) {
17 |
18 | //--------------------------------------------------------------------------
19 | //
20 | // Variables
21 | //
22 | //--------------------------------------------------------------------------
23 |
24 | _focusedItem: string;
25 |
26 | _items = ["tab", "between", "us"];
27 |
28 | //--------------------------------------------------------------------------
29 | //
30 | // Public Methods
31 | //
32 | //--------------------------------------------------------------------------
33 |
34 | render() {
35 | return (
36 |
37 |
44 | Tab
45 |
46 |
53 | between
54 |
55 |
62 | us
63 |
64 |
65 | );
66 | }
67 |
68 | //--------------------------------------------------------------------------
69 | //
70 | // Private Methods
71 | //
72 | //--------------------------------------------------------------------------
73 |
74 | _itemToStyles(id: string): HashMap {
75 | // using inline-styles for demo purposes only
76 | return {
77 | "font-weight": this._focusedItem === id ? "bolder" : ""
78 | };
79 | }
80 |
81 | private _handleFocus(event: FocusEvent): void {
82 | const node = event.currentTarget as Element;
83 | this._focusedItem = node.getAttribute("data-item-id");
84 | }
85 |
86 | private _handleBlur(event: FocusEvent): void {
87 | const node = event.currentTarget as Element;
88 |
89 | if (this._focusedItem === node.getAttribute("data-item-id")) {
90 | this._focusedItem = null;
91 | }
92 | }
93 |
94 | }
95 |
96 | export = HandlingChildrenEvents;
97 |
--------------------------------------------------------------------------------
/patterns/HandlingMultipleEvents.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-handling-multiple-events",
12 | item: "esri-handling-multiple-events__item"
13 | };
14 |
15 | @subclass("examples.HandlingMultipleEvents")
16 | class HandlingMultipleEvents extends declared(Widget) {
17 |
18 | //--------------------------------------------------------------------------
19 | //
20 | // Variables
21 | //
22 | //--------------------------------------------------------------------------
23 |
24 | _focusedItem: string;
25 |
26 | _items = ["tab", "between", "us"];
27 |
28 | //--------------------------------------------------------------------------
29 | //
30 | // Public Methods
31 | //
32 | //--------------------------------------------------------------------------
33 |
34 | render() {
35 | return (
36 |
37 |
44 | Tab
45 |
46 |
53 | between
54 |
55 |
62 | us
63 |
64 |
65 | );
66 | }
67 |
68 | //--------------------------------------------------------------------------
69 | //
70 | // Private Methods
71 | //
72 | //--------------------------------------------------------------------------
73 |
74 | private _itemToStyles(id: string): HashMap {
75 | // using inline-styles for demo purposes only
76 | return {
77 | "font-weight": this._focusedItem === id ? "bolder" : ""
78 | };
79 | }
80 |
81 | private _handleFocus(event: FocusEvent): void {
82 | const node = event.currentTarget as Element;
83 | this._focusedItem = node.getAttribute("data-item-id");
84 | }
85 |
86 | private _handleBlur(event: FocusEvent): void {
87 | const node = event.currentTarget as Element;
88 |
89 | if (this._focusedItem === node.getAttribute("data-item-id")) {
90 | this._focusedItem = null;
91 | }
92 | }
93 |
94 | }
95 |
96 | export = HandlingMultipleEvents;
97 |
--------------------------------------------------------------------------------
/patterns/InnerHTML.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-innerhtml"
12 | };
13 |
14 | @subclass("examples.InnerHTML")
15 | class InnerHTML extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Public Methods
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | render() {
24 | const someExternalContent = "I am bold! ";
25 |
26 | return (
27 |
28 | );
29 | }
30 |
31 | }
32 |
33 | export = InnerHTML;
34 |
--------------------------------------------------------------------------------
/patterns/ManagingListItemFocus.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 |
5 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
6 |
7 | import Widget = require("esri/widgets/Widget");
8 |
9 | import {renderable, tsx} from "esri/widgets/support/widget";
10 |
11 | const UP_ARROW = 38;
12 | const DOWN_ARROW = 40;
13 |
14 | const CSS = {
15 | base: "esri-hello-list",
16 | item: "esri-hello-list__item",
17 | text: "esri-hello-list__item-text"
18 | };
19 |
20 | @subclass("examples.ManagingListItemFocus")
21 | class ManagingListItemFocus extends declared(Widget) {
22 |
23 | //--------------------------------------------------------------------------
24 | //
25 | // Variables
26 | //
27 | //--------------------------------------------------------------------------
28 |
29 | _focusIndex = 0;
30 |
31 | //--------------------------------------------------------------------------
32 | //
33 | // Properties
34 | //
35 | //--------------------------------------------------------------------------
36 |
37 | //----------------------------------
38 | // items
39 | //----------------------------------
40 |
41 | @property()
42 | @renderable()
43 | items: string[] = ["Jerry", "George", "Elaine", "Kramer"];
44 |
45 | //--------------------------------------------------------------------------
46 | //
47 | // Public Methods
48 | //
49 | //--------------------------------------------------------------------------
50 |
51 | render() {
52 | return (
53 |
58 | { this.items.map(this._simpleToComplexItem) }
59 |
60 | );
61 | }
62 |
63 | //--------------------------------------------------------------------------
64 | //
65 | // Private Methods
66 | //
67 | //--------------------------------------------------------------------------
68 |
69 | private _handleAfterUpdate(node: any, options: any, selector: string, props: any, children: any[]): void {
70 | const focusIndex = this._focusIndex;
71 |
72 | if (children[focusIndex]) {
73 | children[focusIndex].domNode.focus();
74 | }
75 | }
76 |
77 | private _handleListClick(event: MouseEvent): void {
78 | const item = (event.target as HTMLInputElement);
79 | const list = (event.currentTarget as HTMLOListElement);
80 | const children = Array.prototype.slice.apply(list.children);
81 |
82 | this._focusIndex = children.indexOf(item);
83 | }
84 |
85 | private _handleKeyDown(event: KeyboardEvent): void {
86 | if (event.keyCode !== UP_ARROW && event.keyCode !== DOWN_ARROW) {
87 | return;
88 | }
89 |
90 | let nextIndex: number;
91 | const numItems = this.items.length - 1;
92 |
93 | if (event.keyCode === UP_ARROW) {
94 | nextIndex = this._focusIndex - 1;
95 | nextIndex = nextIndex < 0 ? 0 : nextIndex;
96 | }
97 |
98 | if (event.keyCode === DOWN_ARROW) {
99 | nextIndex = this._focusIndex + 1;
100 | nextIndex = nextIndex > numItems ? numItems : nextIndex;
101 | }
102 |
103 | this._focusIndex = nextIndex;
104 | }
105 |
106 | private _simpleToComplexItem(item: string): any {
107 | return (
108 |
111 | {item}
112 |
113 | );
114 | }
115 |
116 | }
117 |
118 | export = ManagingListItemFocus;
119 |
--------------------------------------------------------------------------------
/patterns/NodeCreation.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {renderable, tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-node-creation",
12 | innerChild: "esri-node-creation__inner-child",
13 | outerChild: "esri-node-creation__outer-child"
14 | };
15 |
16 | @subclass("examples.AccessibleNodes")
17 | class AccessibleNodes extends declared(Widget) {
18 |
19 | //--------------------------------------------------------------------------
20 | //
21 | // Lifecycle
22 | //
23 | //--------------------------------------------------------------------------
24 |
25 | postInitialize() {
26 | this._intervalId = setInterval(() => {
27 | this.text = this._options[this._currentOptionIndex++ % this._options.length];
28 | }, 500);
29 | }
30 |
31 | destroy() {
32 | clearInterval(this._intervalId);
33 | }
34 |
35 | //--------------------------------------------------------------------------
36 | //
37 | // Variables
38 | //
39 | //--------------------------------------------------------------------------
40 |
41 | private _options = ["first", "second", "third"];
42 | private _currentOptionIndex = 0;
43 | private _intervalId: number;
44 |
45 | //--------------------------------------------------------------------------
46 | //
47 | // Properties
48 | //
49 | //--------------------------------------------------------------------------
50 |
51 | //----------------------------------
52 | // text
53 | //----------------------------------
54 |
55 | @property()
56 | @renderable()
57 | text: string = this._options[this._currentOptionIndex++];
58 |
59 | //--------------------------------------------------------------------------
60 | //
61 | // Public Methods
62 | //
63 | //--------------------------------------------------------------------------
64 |
65 | render() {
66 | const childContent = this.text === "first" ? (
67 | {this.text}
68 | ) : this.text;
69 |
70 | return (
71 |
72 |
{
73 | childContent
74 | }
75 |
76 | );
77 | }
78 |
79 | //--------------------------------------------------------------------------
80 | //
81 | // Private Methods
82 | //
83 | //--------------------------------------------------------------------------
84 |
85 | private _afterParentCreate(): void {
86 | console.log("root created");
87 | }
88 |
89 | private _afterOuterChildCreate(): void {
90 | console.log("outer child created");
91 | }
92 |
93 | private _afterInnerChildCreate(): void {
94 | console.log("inner child created");
95 | }
96 |
97 | }
98 |
99 | export = AccessibleNodes;
100 |
--------------------------------------------------------------------------------
/patterns/ReferencingNodesOnDemand.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-referencing-nodes-on-demand"
12 | };
13 |
14 | @subclass("examples.ReferencingNodesOnDemand")
15 | class AccessibleNodes extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Variables
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | private _nodes: HTMLElement[] = [];
24 |
25 | private _nodeIndex = 0;
26 |
27 | //--------------------------------------------------------------------------
28 | //
29 | // Public Methods
30 | //
31 | //--------------------------------------------------------------------------
32 |
33 | render() {
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | Focus on
43 |
44 | 1st
45 | 2nd
46 | 3rd
47 |
48 | child
49 |
50 |
51 | Do it!
52 |
53 |
54 | );
55 | }
56 |
57 | //--------------------------------------------------------------------------
58 | //
59 | // Private Methods
60 | //
61 | //--------------------------------------------------------------------------
62 |
63 | private _focusNode(): void {
64 | this._nodes[this._nodeIndex].focus();
65 | }
66 |
67 | private _storeNode(node: HTMLElement): void {
68 | const index = node["data-child-index"] as number;
69 | this._nodes[index] = node;
70 | }
71 |
72 | private _storeNodeIndex(event: Event): void {
73 | const node = event.target as HTMLSelectElement;
74 | this._nodeIndex = node.selectedIndex;
75 | }
76 |
77 | }
78 |
79 | export = AccessibleNodes;
80 |
--------------------------------------------------------------------------------
/patterns/RenderingArrayItems.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {renderable, tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-rendering-array-items",
12 | item: "esri-rendering-array-items__item",
13 | text: "esri-rendering-array-items__item-text"
14 | };
15 |
16 | @subclass("examples.RenderingArrayItems")
17 | class RenderingArrayItems extends declared(Widget) {
18 |
19 | //--------------------------------------------------------------------------
20 | //
21 | // Properties
22 | //
23 | //--------------------------------------------------------------------------
24 |
25 | //----------------------------------
26 | // items
27 | //----------------------------------
28 |
29 | @property()
30 | @renderable()
31 | items: string[] = ["Jerry", "George", "Elaine", "Kramer"];
32 |
33 | //--------------------------------------------------------------------------
34 | //
35 | // Public Methods
36 | //
37 | //--------------------------------------------------------------------------
38 |
39 | render() {
40 | return (
41 |
42 | {
43 | this.items.map((item, key) =>
44 |
47 | {item}
48 |
49 | )
50 | }
51 |
52 | );
53 | }
54 |
55 | }
56 |
57 | export = RenderingArrayItems;
58 |
--------------------------------------------------------------------------------
/patterns/RenderingCollectionItems.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import Collection = require("esri/core/Collection");
7 |
8 | import Widget = require("esri/widgets/Widget");
9 |
10 | import {renderable, tsx} from "esri/widgets/support/widget";
11 |
12 | const CSS = {
13 | base: "esri-rendering-collection-items",
14 | item: "esri-rendering-collection-items__item",
15 | text: "esri-rendering-collection-items__item-text"
16 | };
17 |
18 | @subclass("examples.RenderingCollectionItems")
19 | class RenderingCollectionItems extends declared(Widget) {
20 |
21 | //--------------------------------------------------------------------------
22 | //
23 | // Lifecycle
24 | //
25 | //--------------------------------------------------------------------------
26 |
27 | postInitialize() {
28 |
29 | setInterval(() => {
30 | const kramer = "Kramer";
31 |
32 | // @renderable schedules rendering when the collection is modified
33 |
34 | if (this.items.indexOf(kramer) === -1) {
35 | this.items.add(kramer);
36 | }
37 | else {
38 | this.items.remove(kramer);
39 | }
40 | }, 500);
41 | }
42 |
43 | //--------------------------------------------------------------------------
44 | //
45 | // Properties
46 | //
47 | //--------------------------------------------------------------------------
48 |
49 | //----------------------------------
50 | // items
51 | //----------------------------------
52 |
53 | @property()
54 | @renderable()
55 | // TODO: update to Collection once typings support generics
56 | items: Collection = new Collection(["Jerry", "George", "Elaine", "Kramer"]);
57 |
58 | //--------------------------------------------------------------------------
59 | //
60 | // Public Methods
61 | //
62 | //--------------------------------------------------------------------------
63 |
64 | render() {
65 | const items = this.items.toArray();
66 |
67 | return (
68 |
69 | {
70 | items.map((item, key) =>
71 |
74 | {item}
75 |
76 | )
77 | }
78 |
79 | );
80 | }
81 |
82 | }
83 |
84 | export = RenderingCollectionItems;
85 |
--------------------------------------------------------------------------------
/patterns/RoleAttribute.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jcfranco/4x-widget-patterns/b67ba53f1cfb794aba3ccb7ab5c6b26ea5339633/patterns/RoleAttribute.tsx
--------------------------------------------------------------------------------
/patterns/SchedulingRender.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-scheduling-render"
12 | };
13 |
14 | @subclass("examples.SchedulingRender")
15 | class SchedulingRender extends declared(Widget) {
16 |
17 | //--------------------------------------------------------------------------
18 | //
19 | // Lifecycle
20 | //
21 | //--------------------------------------------------------------------------
22 |
23 | postInitialize() {
24 | setInterval(() => {
25 |
26 | // variable is not renderable, so we schedule rendering after modifying it
27 | this._working = !this._working;
28 | this.scheduleRender();
29 |
30 | }, 1000);
31 | }
32 |
33 | //--------------------------------------------------------------------------
34 | //
35 | // Variables
36 | //
37 | //--------------------------------------------------------------------------
38 |
39 | private _working = false;
40 |
41 | //--------------------------------------------------------------------------
42 | //
43 | // Public Methods
44 | //
45 | //--------------------------------------------------------------------------
46 |
47 | render() {
48 | const label = this._working ? "working" : "!working";
49 |
50 | return (
51 | {label}
52 | );
53 | }
54 |
55 | }
56 |
57 | export = SchedulingRender;
58 |
--------------------------------------------------------------------------------
/patterns/SpreadingAttributes.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | import {subclass, declared} from "esri/core/accessorSupport/decorators";
6 |
7 | import Widget = require("esri/widgets/Widget");
8 |
9 | import {tsx} from "esri/widgets/support/widget";
10 |
11 | const CSS = {
12 | base: "esri-spreading-attributes"
13 | };
14 |
15 | @subclass("examples.SpreadingAttributes")
16 | class SpreadingAttributes extends declared(Widget) {
17 |
18 | private _propsToSpread: any = {
19 | "aria-role": "button",
20 | tabIndex: "0",
21 | title: "static-title"
22 | }
23 |
24 | //--------------------------------------------------------------------------
25 | //
26 | // Public Methods
27 | //
28 | //--------------------------------------------------------------------------
29 |
30 | render() {
31 | return (
32 | Check out my attributes!
33 | );
34 | }
35 |
36 | }
37 |
38 | export = SpreadingAttributes;
39 |
--------------------------------------------------------------------------------
/patterns/SyntheticEvents.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import Widget = require("esri/widgets/Widget");
5 |
6 | import {
7 | declared,
8 | property,
9 | subclass
10 | } from "esri/core/accessorSupport/decorators";
11 |
12 | import {tsx, renderable} from "esri/widgets/support/widget";
13 |
14 | const CSS = {
15 | base: "esri-synthetic-events",
16 | header: "esri-synthetic-events__header",
17 | input: "esri-synthetic-events__input",
18 | footer: "esri-synthetic-events__footer"
19 | };
20 |
21 | @subclass("examples.SyntheticEvents")
22 | class SyntheticEvents extends declared(Widget) {
23 |
24 | //--------------------------------------------------------------------------
25 | //
26 | // Properties
27 | //
28 | //--------------------------------------------------------------------------
29 |
30 | //----------------------------------
31 | // selectedItem
32 | //----------------------------------
33 |
34 | @property()
35 | @renderable()
36 | selectedNodeType: string = null;
37 |
38 | //--------------------------------------------------------------------------
39 | //
40 | // Public Methods
41 | //
42 | //--------------------------------------------------------------------------
43 |
44 | render() {
45 | const lastSelection = this.selectedNodeType ? this.selectedNodeType : "n/a";
46 |
47 | return (
48 |
51 |
56 |
61 |
65 |
66 | );
67 | }
68 |
69 | //--------------------------------------------------------------------------
70 | //
71 | // Private Methods
72 | //
73 | //--------------------------------------------------------------------------
74 |
75 | private _handleClick(event: MouseEvent): void {
76 | const node = event.target as HTMLElement;
77 | const nodeName = node.nodeName;
78 |
79 | this.selectedNodeType = nodeName;
80 |
81 | this.emit("item-selected", {
82 | selectedItemNode: nodeName
83 | });
84 | }
85 |
86 | }
87 |
88 | export = SyntheticEvents;
89 |
--------------------------------------------------------------------------------
/patterns/TogglingNodeVisibility.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 |
6 | import Widget = require("esri/widgets/Widget");
7 |
8 | import {renderable, tsx} from "esri/widgets/support/widget";
9 |
10 | const CSS = {
11 | base: "esri-toggling-node-visibility",
12 | header: "esri-toggling-node-visibility__header",
13 | content: "esri-toggling-node-visibility__content",
14 | footer: "esri-toggling-node-visibility__footer"
15 | };
16 |
17 | @subclass("examples.TogglingNodeVisibility")
18 | class TogglingNodeVisibility extends declared(Widget) {
19 |
20 | //--------------------------------------------------------------------------
21 | //
22 | // Properties
23 | //
24 | //--------------------------------------------------------------------------
25 |
26 | //----------------------------------
27 | // headerVisible
28 | //----------------------------------
29 |
30 | @property()
31 | @renderable()
32 | headerVisible = true;
33 |
34 | //----------------------------------
35 | // contentVisible
36 | //----------------------------------
37 |
38 | @property()
39 | @renderable()
40 | contentVisible = true;
41 |
42 | //----------------------------------
43 | // footerVisible
44 | //----------------------------------
45 |
46 | @property()
47 | @renderable()
48 | footerVisible = true;
49 |
50 | //--------------------------------------------------------------------------
51 | //
52 | // Public Methods
53 | //
54 | //--------------------------------------------------------------------------
55 |
56 | render() {
57 | const headerNode = this.headerVisible ? (
58 |
59 | ) : null;
60 |
61 | const contentNode = this.contentVisible ? (
62 | content
63 | ) : null;
64 |
65 | const footerNode = this.footerVisible ? (
66 |
67 | ) : null;
68 |
69 | return (
70 |
71 | {headerNode}
72 | {contentNode}
73 | {footerNode}
74 |
75 | );
76 | }
77 |
78 | }
79 |
80 | export = TogglingNodeVisibility;
81 |
--------------------------------------------------------------------------------
/patterns/UsingDgrid.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | ///
5 | ///
6 |
7 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
8 |
9 | import Widget = require("esri/widgets/Widget");
10 |
11 | import {renderable, tsx} from "esri/widgets/support/widget";
12 |
13 | declare const OnDemandGrid: any;
14 | declare const Memory: any;
15 |
16 | const CSS = {
17 | base: "esri-using-dgrid",
18 | grid: "esri-using-dgrid__grid"
19 | };
20 |
21 | @subclass("examples.UsingDgrid")
22 | class UsingDgrid extends declared(Widget) {
23 |
24 | //--------------------------------------------------------------------------
25 | //
26 | // Lifecycle
27 | //
28 | //--------------------------------------------------------------------------
29 |
30 | destroy() {
31 | if (this._grid) {
32 | this._grid.destroy();
33 | this._grid = null;
34 | }
35 | }
36 |
37 | //--------------------------------------------------------------------------
38 | //
39 | // Variables
40 | //
41 | //--------------------------------------------------------------------------
42 |
43 | _grid: any = null;
44 |
45 | _items: any = new Memory({
46 | idProperty: "firstName",
47 | data: [
48 | {
49 | firstName: "Jerry",
50 | lastName: "Seinfeld"
51 | },
52 | {
53 | firstName: "George",
54 | lastName: "Costanza"
55 | },
56 | {
57 | firstName: "Elaine",
58 | lastName: "Benes"
59 | },
60 | {
61 | firstName: "Cosmo",
62 | lastName: "Kramer"
63 | }
64 | ]
65 | });
66 |
67 | //--------------------------------------------------------------------------
68 | //
69 | // Properties
70 | //
71 | //--------------------------------------------------------------------------
72 |
73 | //----------------------------------
74 | // gridVisible
75 | //----------------------------------
76 |
77 | @property()
78 | @renderable()
79 | gridVisible = true;
80 |
81 | //--------------------------------------------------------------------------
82 | //
83 | // Public Methods
84 | //
85 | //--------------------------------------------------------------------------
86 |
87 | render() {
88 | const gridNode = this.gridVisible ? (
89 |
91 | ) : null;
92 |
93 | return (
94 |
95 | {gridNode}
96 |
97 | );
98 | }
99 |
100 | //--------------------------------------------------------------------------
101 | //
102 | // Private Methods
103 | //
104 | //--------------------------------------------------------------------------
105 |
106 | private _handleAfterCreate(node: Element): void {
107 | if (!this._grid) {
108 | this._grid = new OnDemandGrid({
109 | className: CSS.grid,
110 | collection: this._items,
111 | columns: [
112 | {label: "First Name", field: "firstName", sortable: true},
113 | {label: "Last Name", field: "lastName"}
114 | ]
115 | }, node);
116 |
117 | this._grid.on("click", (event: any) => this.emit("click", event));
118 |
119 | return;
120 | }
121 |
122 | node.appendChild(this._grid.domNode);
123 | }
124 |
125 | }
126 |
127 | export = UsingDgrid;
128 |
--------------------------------------------------------------------------------
/patterns/UsingDijit.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | ///
5 | ///
6 |
7 | ///
8 |
9 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
10 |
11 | import Widget = require("esri/widgets/Widget");
12 |
13 | import {renderable, tsx} from "esri/widgets/support/widget";
14 |
15 | declare const Button: any;
16 |
17 | // using any since not all dijits have type definitions
18 | // submitting type definitions to https://github.com/dojo/typings is encouraged
19 | declare const HorizontalSlider: any;
20 |
21 | const CSS = {
22 | base: "esri-using-dijit",
23 | button: "esri-using-dijit__button",
24 | slider: "esri-using-dijit__slider"
25 | };
26 |
27 | function randomNumber(): number {
28 | return Math.floor(Math.random() * 100);
29 | }
30 |
31 | @subclass("examples.UsingDijit")
32 | class UsingDijit extends declared(Widget) {
33 |
34 | //--------------------------------------------------------------------------
35 | //
36 | // Lifecycle
37 | //
38 | //--------------------------------------------------------------------------
39 |
40 | destroy() {
41 | if (this._button) {
42 | this._button.destroy();
43 | this._button = null;
44 | }
45 |
46 | if (this._slider) {
47 | this._slider.destroy();
48 | this._slider = null;
49 | }
50 | }
51 |
52 | //--------------------------------------------------------------------------
53 | //
54 | // Variables
55 | //
56 | //--------------------------------------------------------------------------
57 |
58 | private _button: any;
59 | private _slider: any;
60 |
61 | //--------------------------------------------------------------------------
62 | //
63 | // Properties
64 | //
65 | //--------------------------------------------------------------------------
66 |
67 | //----------------------------------
68 | // minValue
69 | //----------------------------------
70 |
71 | @property()
72 | @renderable()
73 | minValue = 0;
74 |
75 | //----------------------------------
76 | // maxValue
77 | //----------------------------------
78 |
79 | @property()
80 | @renderable()
81 | maxValue = 100;
82 |
83 | //----------------------------------
84 | // value
85 | //----------------------------------
86 |
87 | @property()
88 | @renderable()
89 | value = 50;
90 |
91 | //----------------------------------
92 | // randomNumber
93 | //----------------------------------
94 |
95 | @property({
96 | readOnly: true
97 | })
98 | @renderable()
99 | randomNumber = randomNumber();
100 |
101 | //--------------------------------------------------------------------------
102 | //
103 | // Public Methods
104 | //
105 | //--------------------------------------------------------------------------
106 |
107 | render() {
108 | return (
109 |
110 |
111 |
{this.randomNumber}
112 |
113 |
114 | {this.value}
115 |
116 |
117 | );
118 | }
119 |
120 | //--------------------------------------------------------------------------
121 | //
122 | // Private Methods
123 | //
124 | //--------------------------------------------------------------------------
125 |
126 | private _createButton(node: Element): void {
127 | this._button = new Button({
128 | class: CSS.button,
129 | label: "I am a dijit button",
130 | onClick: () => {
131 | console.log("dijit button was clicked");
132 | this._set("randomNumber", randomNumber());
133 | // this.scheduleRender();
134 | }
135 | }, node);
136 |
137 | this._button.startup();
138 | }
139 |
140 | private _createSlider(node: Element): void {
141 | this._slider = new HorizontalSlider({
142 | class: CSS.slider,
143 | intermediateChanges: true,
144 | minimum: this.minValue,
145 | maximum: this.maxValue,
146 | value: this.value,
147 | onChange: (value: number) => {
148 | this.value = value;
149 | }
150 | }, node);
151 |
152 | this._slider.startup();
153 | }
154 |
155 | }
156 |
157 | export = UsingDijit;
158 |
--------------------------------------------------------------------------------
/patterns/WorkingWithSVG.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import {subclass, declared, property} from "esri/core/accessorSupport/decorators";
5 | import {renderable, tsx} from "esri/widgets/support/widget";
6 |
7 | import Widget = require("esri/widgets/Widget");
8 |
9 | import Color = require("esri/Color");
10 |
11 | const CSS = {
12 | base: "esri-working-with-svg"
13 | };
14 |
15 | @subclass("examples.WorkingWithSVG")
16 | class WorkingWithSVG extends declared(Widget) {
17 |
18 | //--------------------------------------------------------------------------
19 | //
20 | // Lifecycle
21 | //
22 | //--------------------------------------------------------------------------
23 |
24 | postInitialize() {
25 |
26 | // changing colors for demo purposes
27 | setInterval(() => {
28 | this._currentColorIndex = (this._currentColorIndex + 1) % this._demoColors.length;
29 | this.color = new Color(this._demoColors[this._currentColorIndex]);
30 | }, 800);
31 | }
32 |
33 | //--------------------------------------------------------------------------
34 | //
35 | // Variables
36 | //
37 | //--------------------------------------------------------------------------
38 |
39 | private _currentColorIndex = 0;
40 |
41 | private _demoColors = [
42 | "#E44D26",
43 | "97270A",
44 | "9DE83E",
45 | "0C34B1",
46 | "123297"
47 | ];
48 |
49 | //--------------------------------------------------------------------------
50 | //
51 | // Properties
52 | //
53 | //--------------------------------------------------------------------------
54 |
55 | //----------------------------------
56 | // color
57 | //----------------------------------
58 |
59 | @property({
60 | type: Color
61 | })
62 | @renderable()
63 | color = new Color(this._demoColors[this._currentColorIndex]);
64 |
65 | //----------------------------------
66 | // headerVisible
67 | //----------------------------------
68 |
69 | @property()
70 | @renderable()
71 | headerVisible = true;
72 |
73 | //--------------------------------------------------------------------------
74 | //
75 | // Public Methods
76 | //
77 | //--------------------------------------------------------------------------
78 |
79 | render() {
80 | const hex = this.color.toHex();
81 |
82 | const header = this.headerVisible ? (
83 |
84 |
85 |
86 |
87 |
88 |
89 | ) : null;
90 |
91 | return (
92 |
93 | {/* source: https://www.w3.org/html/logo/ */}
94 |
95 | HTML5 Logo
96 |
97 | {header}
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | );
108 | }
109 |
110 | }
111 |
112 | export = WorkingWithSVG;
113 |
--------------------------------------------------------------------------------
/patterns/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "patterns": [
3 | "AccessibleNodes",
4 | "Binding",
5 | "BooleanAttributes",
6 | "CleaningUp",
7 | "CustomEventHandlerArguments",
8 | "DistinguishableChildren",
9 | "DealingWithWhitespace",
10 | "DefiningGettersAndSetters",
11 | "HandlingChildrenEvents",
12 | "HandlingMultipleEvents",
13 | "InnerHTML",
14 | "ManagingListItemFocus",
15 | "NodeCreation",
16 | "ReferencingNodesOnDemand",
17 | "RenderingArrayItems",
18 | "RenderingCollectionItems",
19 | "RoleAttribute",
20 | "SchedulingRender",
21 | "SpreadingAttributes",
22 | "SyntheticEvents",
23 | "TogglingNodeVisibility",
24 | "UsingDgrid",
25 | "UsingDijit",
26 | "WorkingWithSVG"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/patterns/support/loadStylesheet.ts:
--------------------------------------------------------------------------------
1 | import Require = dojo.Require;
2 |
3 | function loadStylesheet(url: string, done: Function): void {
4 | const link = document.createElement("link");
5 | link.type = "text/css";
6 | link.rel = "stylesheet";
7 | link.href = url;
8 |
9 | link.onload = (): void => {
10 | link.onload = null;
11 | link.onerror = null;
12 | done(link);
13 | };
14 |
15 | link.onerror = (): void => {
16 | link.onload = null;
17 | link.onerror = null;
18 | done(link);
19 | };
20 |
21 | document.head.appendChild(link);
22 | }
23 |
24 | const plugin = {
25 | load(name: string, require: Require, done: Function): void {
26 | loadStylesheet(require.toUrl(name), done);
27 | }
28 | };
29 |
30 | export = plugin;
31 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "amd",
4 | "noImplicitAny": true,
5 | "jsx": "react",
6 | "jsxFactory": "tsx",
7 | "target": "es5",
8 | "experimentalDecorators": true,
9 | "preserveConstEnums": true,
10 | "suppressImplicitAnyIndexErrors": true
11 | },
12 | "files": [
13 | "node_modules/dojo-typings/dojo/1.11/index.d.ts",
14 | "node_modules/dojo-typings/dojo/1.11/modules.d.ts",
15 | "node_modules/dojo-typings/dojo/1.11/loader.d.ts",
16 |
17 | "node_modules/dojo-typings/dijit/1.11/index.d.ts",
18 | "node_modules/dojo-typings/dijit/1.11/modules.d.ts",
19 |
20 | "node_modules/dojo-typings/dojox/1.11/index.d.ts",
21 | "node_modules/dojo-typings/dojox/1.11/modules.d.ts"
22 | ],
23 | "include": [
24 | "browser/",
25 | "patterns/",
26 | "typings/"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/typings/arcgis-js-api-extensions.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace __esri {
2 |
3 | interface promiseUtils {
4 | timeout(promise: IPromise, timeout: number, timeoutRejectValue: any): IPromise;
5 | }
6 |
7 | }
8 |
--------------------------------------------------------------------------------