Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit 5d3439c

Browse files
committed
Initial Commit
1 parent 99b929d commit 5d3439c

14 files changed

Lines changed: 6881 additions & 0 deletions

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
6+
# generated directory
7+
**/generated
8+
9+
# output directory
10+
/out
11+
12+
#object folder
13+
/obj
14+
15+
#vs folder
16+
/.vs
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<manifest>
3+
<control namespace="AB" constructor="CompositeAddress" version="0.0.21" display-name-key="Composite Address Control" description-key="Use this control to minimize the size of address section on the form and format it the way you want" control-type="standard">
4+
<property name="value" display-name-key="Full Address" description-key="Full Address" of-type="SingleLine.Text" usage="bound" required="true" />
5+
<property name="format" display-name-key="Address Formatting" description-key="Provide pattern to format Full Address" of-type="SingleLine.Text" usage="input" required="true" />
6+
<property name="street1" display-name-key="Street 1" description-key="Street 1" of-type="SingleLine.Text" usage="bound" required="true" />
7+
<property name="street2" display-name-key="Street 2" description-key="Street 2" of-type="SingleLine.Text" usage="bound" required="false" />
8+
<property name="street2visible" usage="input" of-type="Enum" display-name-key="Street 2 is Visible" description-key="Select if Street 2 field is visible" required="true">
9+
<value name="yes" display-name-key="Yes" description-key="Yes, Street 2 is visible" default="true">yes</value>
10+
<value name="no" display-name-key="No" description-key="No, Street 2 is not visible">no</value>
11+
</property>
12+
<property name="street3" display-name-key="Street 3" description-key="Street 3" of-type="SingleLine.Text" usage="bound" required="false" />
13+
<property name="street3visible" usage="input" of-type="Enum" display-name-key="Street 3 is Visible" description-key="Select if Street 3 field is visible" required="true">
14+
<value name="yes" display-name-key="Yes" description-key="Yes, Street 3 is visible" default="true">yes</value>
15+
<value name="no" display-name-key="No" description-key="No, Street 3 is not visible">no</value>
16+
</property>
17+
<property name="city" display-name-key="City" description-key="City" of-type="SingleLine.Text" usage="bound" required="true" />
18+
<property name="county" display-name-key="County" description-key="County" of-type="SingleLine.Text" usage="bound" required="false" />
19+
<property name="countyvisible" usage="input" of-type="Enum" display-name-key="County is Visible" description-key="Select if County field is visible" required="true">
20+
<value name="yes" display-name-key="Yes" description-key="Yes, County is visible" default="true">yes</value>
21+
<value name="no" display-name-key="No" description-key="No, County is not visible">no</value>
22+
</property>
23+
<type-group name="StringOrOptionset">
24+
<type>SingleLine.Text</type>
25+
<type>OptionSet</type>
26+
</type-group>
27+
<property name="state" display-name-key="State/Province" description-key="State/Province" of-type-group="StringOrOptionset" usage="bound" required="false" />
28+
<property name="statevisible" usage="input" of-type="Enum" display-name-key="State/Province is Visible" description-key="Select if State/Province field is visible" required="true">
29+
<value name="yes" display-name-key="Yes" description-key="Yes, State/Province is visible" default="true">yes</value>
30+
<value name="no" display-name-key="No" description-key="No, State/Province is not visible">no</value>
31+
</property>
32+
<property name="zipcode" display-name-key="Zip Code" description-key="Zip Code" of-type="SingleLine.Text" usage="bound" required="true" />
33+
<property name="country" display-name-key="Country" description-key="Country" of-type-group="StringOrOptionset" usage="bound" required="false" />
34+
<property name="countryvisible" usage="input" of-type="Enum" display-name-key="Country is Visible" description-key="Select if Country field is visible" required="true">
35+
<value name="yes" display-name-key="Yes" description-key="Yes, Country is visible" default="true">yes</value>
36+
<value name="no" display-name-key="No" description-key="No, Country is not visible">no</value>
37+
</property>
38+
<resources>
39+
<code path="index.ts" order="1"/>
40+
<css path="../node_modules/bootstrap/dist/css/bootstrap.min.css" order="1" />
41+
<css path="css/CompositeAddress.css" order="2" />
42+
</resources>
43+
</control>
44+
</manifest>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
.compositeAddress {
2+
background: transparent;
3+
border-style: none;
4+
color: #000000;
5+
font-size: 1.0rem;
6+
font-weight: 600;
7+
height: 2.5rem;
8+
line-height: 2.5rem;
9+
width: 98%;
10+
padding-left: 0.5em;
11+
padding-right: 0.5em;
12+
outline: 0 !important;
13+
}
14+
15+
.compositeAddressFocused {
16+
background: transparent;
17+
border-width: 1px;
18+
border-color: #000000;
19+
color: #000000;
20+
font-size: 1.0rem;
21+
height: 2.5rem;
22+
line-height: 2.5rem;
23+
width: 98%;
24+
padding-left: 0.5em;
25+
padding-right: 0.5em;
26+
outline: 0 !important;
27+
}
28+
29+
.d365StyleInput {
30+
background: transparent;
31+
border-width: 1px;
32+
border-color: #000000;
33+
color: #000000;
34+
font-size: 1.0rem;
35+
height: 2.5rem;
36+
line-height: 2.5rem;
37+
padding-left: 0.5em;
38+
padding-right: 0.5em;
39+
outline: 0 !important;
40+
}

CompositeAddress/index.ts

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import { IInputs, IOutputs } from "./generated/ManifestTypes";
2+
import * as $ from "jquery";
3+
import * as Bootstrap from "bootstrap";
4+
5+
export class CompositeAddress implements ComponentFramework.StandardControl<IInputs, IOutputs> {
6+
7+
private notifyOutputChanged: () => void;
8+
private mainAddressField: HTMLInputElement;
9+
private fullAddressFormat: string;
10+
private currentValues: any = new Object();
11+
private currentLabels: any = new Object();
12+
private allControls: any = new Object();
13+
14+
constructor() {
15+
16+
}
17+
18+
public init(context: ComponentFramework.Context<IInputs>,
19+
notifyOutputChanged: () => void,
20+
state: ComponentFramework.Dictionary,
21+
container: HTMLDivElement) {
22+
Bootstrap.hasOwnProperty("test");
23+
24+
this.notifyOutputChanged = notifyOutputChanged;
25+
26+
this.fullAddressFormat = context.parameters.format.raw;
27+
28+
this.mainAddressField = document.createElement("input");
29+
this.mainAddressField.className = "compositeAddress";
30+
this.mainAddressField.readOnly = true;
31+
32+
if (context.parameters.value.raw !== null) {
33+
this.mainAddressField.value = context.parameters.value.raw.replace(/\n/gm, ', ');
34+
} else {
35+
this.mainAddressField.value = "---";
36+
}
37+
38+
this.mainAddressField.addEventListener("mouseenter", () => {
39+
this.mainAddressField.className = "compositeAddressFocused";
40+
});
41+
this.mainAddressField.addEventListener("mouseleave", () => {
42+
this.mainAddressField.className = "compositeAddress";
43+
});
44+
45+
container.appendChild(this.mainAddressField);
46+
47+
let popupDiv = document.createElement("div");
48+
49+
this.addControltoPopup(context.parameters.street1, popupDiv, "street1");
50+
51+
if (context.parameters.street2visible.raw === "yes") {
52+
this.addControltoPopup(context.parameters.street2, popupDiv, "street2");
53+
}
54+
55+
if (context.parameters.street3visible.raw === "yes") {
56+
this.addControltoPopup(context.parameters.street3, popupDiv, "street3");
57+
}
58+
59+
this.addControltoPopup(context.parameters.city, popupDiv, "city");
60+
61+
if (context.parameters.countyvisible.raw === "yes") {
62+
this.addControltoPopup(context.parameters.county, popupDiv, "county");
63+
}
64+
65+
if (context.parameters.statevisible.raw === "yes") {
66+
this.addControltoPopup(context.parameters.state, popupDiv, "state");
67+
}
68+
69+
this.addControltoPopup(context.parameters.zipcode, popupDiv, "zipcode");
70+
71+
if (context.parameters.countryvisible.raw === "yes") {
72+
this.addControltoPopup(context.parameters.country, popupDiv, "country");
73+
}
74+
75+
$(this.mainAddressField).popover({
76+
content: popupDiv,
77+
html: true,
78+
placement: "bottom"
79+
});
80+
}
81+
82+
private addControltoPopup(addressProperty: ComponentFramework.PropertyTypes.Property,
83+
container: HTMLDivElement,
84+
controlId: string): void {
85+
let rowDiv = document.createElement("div");
86+
rowDiv.className = "form-group";
87+
container.appendChild(rowDiv);
88+
89+
if (addressProperty.type === "SingleLine.Text") {
90+
let stringAddressProperty = addressProperty as ComponentFramework.PropertyTypes.StringProperty;
91+
92+
let fieldLabel = document.createElement("label");
93+
fieldLabel.innerText = stringAddressProperty.attributes === undefined ? "" : stringAddressProperty.attributes.DisplayName;
94+
rowDiv.appendChild(fieldLabel);
95+
96+
let inputControl = document.createElement("input");
97+
inputControl.id = controlId;
98+
inputControl.className = "form-control d365StyleInput";
99+
inputControl.setAttribute("placeholder", "---");
100+
inputControl.value = stringAddressProperty.raw;
101+
inputControl.addEventListener("change", () => {
102+
let currentText = inputControl.value;
103+
104+
this.currentValues[controlId] = currentText;
105+
this.currentLabels[controlId] = currentText === null ? "" : currentText;
106+
107+
this.formatAddress();
108+
109+
this.notifyOutputChanged();
110+
});
111+
112+
this.currentValues[controlId] = stringAddressProperty.raw;
113+
this.currentLabels[controlId] = stringAddressProperty.formatted;
114+
this.allControls[controlId] = inputControl;
115+
116+
rowDiv.appendChild(inputControl);
117+
} else if (addressProperty.type === "OptionSet") {
118+
let optionsetAddressProperty = addressProperty as ComponentFramework.PropertyTypes.OptionSetProperty;
119+
120+
let fieldLabel = document.createElement("label");
121+
fieldLabel.innerText = optionsetAddressProperty.attributes === undefined
122+
? ""
123+
: optionsetAddressProperty.attributes.DisplayName;
124+
rowDiv.appendChild(fieldLabel);
125+
126+
let selectControl = document.createElement("select");
127+
selectControl.id = controlId;
128+
selectControl.className = "form-control d365StyleInput";
129+
130+
let option: HTMLOptionElement = document.createElement("option");
131+
option.innerHTML = "---Select---";
132+
selectControl.add(option);
133+
134+
if (optionsetAddressProperty.attributes !== undefined) {
135+
optionsetAddressProperty.attributes.Options.forEach(optionRecord => {
136+
option = document.createElement("option");
137+
option.innerHTML = optionRecord.Label;
138+
option.value = optionRecord.Value.toString();
139+
140+
if (optionsetAddressProperty.raw === optionRecord.Value) {
141+
option.selected = true;
142+
}
143+
144+
selectControl.add(option);
145+
});
146+
}
147+
148+
selectControl.addEventListener("change", () => {
149+
if (selectControl.value === "") {
150+
this.currentValues[controlId] = null;
151+
this.currentLabels[controlId] = "";
152+
} else {
153+
this.currentValues[controlId] = parseInt(selectControl.value);
154+
this.currentLabels[controlId] = selectControl.options[selectControl.selectedIndex].label;
155+
}
156+
157+
this.formatAddress();
158+
159+
this.notifyOutputChanged();
160+
});
161+
162+
this.currentValues[controlId] = optionsetAddressProperty.raw;
163+
this.currentLabels[controlId] = optionsetAddressProperty.formatted;
164+
this.allControls[controlId] = selectControl;
165+
166+
rowDiv.appendChild(selectControl);
167+
} else {
168+
throw new Error(`Composite Address: can't render control for ${addressProperty.type} type of field`);
169+
}
170+
}
171+
172+
private formatAddress(): void {
173+
let formattedAddress = this.fullAddressFormat;
174+
175+
formattedAddress = formattedAddress.replace(/\\n/gm, '\n');
176+
177+
for (let propName in this.currentLabels) {
178+
let propertyValue = this.currentLabels[propName] === null ? "" : this.currentLabels[propName];
179+
180+
formattedAddress = formattedAddress.replace(`{${propName}}`, propertyValue);
181+
}
182+
183+
this.currentValues.value = formattedAddress;
184+
this.mainAddressField.value = formattedAddress.replace(/\n/gm, ', ');
185+
}
186+
187+
public updateView(context: ComponentFramework.Context<IInputs>): void {
188+
let formatChanged: boolean = false;
189+
190+
context.updatedProperties.forEach(updatedProperty => {
191+
if (!context.parameters.hasOwnProperty(updatedProperty)) {
192+
return ;
193+
}
194+
195+
formatChanged = true;
196+
197+
let updatedPropertyObject = (context.parameters as any)[updatedProperty];
198+
199+
if (this.allControls.hasOwnProperty(updatedProperty)) {
200+
$(this.allControls[updatedProperty]).val(updatedPropertyObject.raw);
201+
}
202+
203+
this.currentValues[updatedProperty] = updatedPropertyObject.raw;
204+
this.currentLabels[updatedProperty] = updatedPropertyObject.formatted;
205+
});
206+
207+
if (formatChanged) {
208+
this.formatAddress();
209+
this.notifyOutputChanged();
210+
}
211+
}
212+
213+
public getOutputs(): IOutputs {
214+
return this.currentValues as IOutputs;
215+
}
216+
217+
public destroy(): void {
218+
// Add code to cleanup control if necessary
219+
}
220+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/bin
2+
/obj
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<PowerAppsTargetsPath>$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\PowerApps</PowerAppsTargetsPath>
5+
</PropertyGroup>
6+
7+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
8+
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.props" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.props')" />
9+
10+
<PropertyGroup>
11+
<ProjectGuid>3ecce81b-4fc4-4969-8051-d95b273ab784</ProjectGuid>
12+
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
13+
<!--Remove TargetFramework when this is available in 16.1-->
14+
<TargetFramework>net462</TargetFramework>
15+
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
16+
</PropertyGroup>
17+
18+
<!-- Solution Packager overrides, un-comment to use: SolutionPackagerType (Managed, Unmanaged, Both)
19+
<PropertyGroup>
20+
<SolutionPackageType>Managed</SolutionPackageType>
21+
</PropertyGroup>
22+
-->
23+
24+
<ItemGroup>
25+
<PackageReference Include="Microsoft.PowerApps.MSBuild.Solution" Version="0.*" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\.gitignore" />
30+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\bin\**" />
31+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\obj\**" />
32+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.cdsproj" />
33+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.cdsproj.user" />
34+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.sln" />
35+
</ItemGroup>
36+
37+
<ItemGroup>
38+
<None Include="$(MSBuildThisFileDirectory)\**" Exclude="@(ExcludeDirectories)" />
39+
</ItemGroup>
40+
41+
<ItemGroup>
42+
<ProjectReference Include="..\CompositeAddress_PCF.pcfproj" />
43+
</ItemGroup>
44+
45+
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
46+
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.targets" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.targets')" />
47+
48+
</Project>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<ImportExportXml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3+
<Entities />
4+
<Roles />
5+
<Workflows />
6+
<FieldSecurityProfiles />
7+
<Templates />
8+
<EntityMaps />
9+
<EntityRelationships />
10+
<OrganizationSettings />
11+
<optionsets />
12+
<CustomControls />
13+
<EntityDataProviders />
14+
<Languages>
15+
<Language>1033</Language>
16+
</Languages>
17+
</ImportExportXml>

0 commit comments

Comments
 (0)