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

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewButenko committed Aug 19, 2019
1 parent 99b929d commit 5d3439c
Show file tree
Hide file tree
Showing 14 changed files with 6,881 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules

# generated directory
**/generated

# output directory
/out

#object folder
/obj

#vs folder
/.vs
44 changes: 44 additions & 0 deletions CompositeAddress/ControlManifest.Input.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<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">
<property name="value" display-name-key="Full Address" description-key="Full Address" of-type="SingleLine.Text" usage="bound" required="true" />
<property name="format" display-name-key="Address Formatting" description-key="Provide pattern to format Full Address" of-type="SingleLine.Text" usage="input" required="true" />
<property name="street1" display-name-key="Street 1" description-key="Street 1" of-type="SingleLine.Text" usage="bound" required="true" />
<property name="street2" display-name-key="Street 2" description-key="Street 2" of-type="SingleLine.Text" usage="bound" required="false" />
<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">
<value name="yes" display-name-key="Yes" description-key="Yes, Street 2 is visible" default="true">yes</value>
<value name="no" display-name-key="No" description-key="No, Street 2 is not visible">no</value>
</property>
<property name="street3" display-name-key="Street 3" description-key="Street 3" of-type="SingleLine.Text" usage="bound" required="false" />
<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">
<value name="yes" display-name-key="Yes" description-key="Yes, Street 3 is visible" default="true">yes</value>
<value name="no" display-name-key="No" description-key="No, Street 3 is not visible">no</value>
</property>
<property name="city" display-name-key="City" description-key="City" of-type="SingleLine.Text" usage="bound" required="true" />
<property name="county" display-name-key="County" description-key="County" of-type="SingleLine.Text" usage="bound" required="false" />
<property name="countyvisible" usage="input" of-type="Enum" display-name-key="County is Visible" description-key="Select if County field is visible" required="true">
<value name="yes" display-name-key="Yes" description-key="Yes, County is visible" default="true">yes</value>
<value name="no" display-name-key="No" description-key="No, County is not visible">no</value>
</property>
<type-group name="StringOrOptionset">
<type>SingleLine.Text</type>
<type>OptionSet</type>
</type-group>
<property name="state" display-name-key="State/Province" description-key="State/Province" of-type-group="StringOrOptionset" usage="bound" required="false" />
<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">
<value name="yes" display-name-key="Yes" description-key="Yes, State/Province is visible" default="true">yes</value>
<value name="no" display-name-key="No" description-key="No, State/Province is not visible">no</value>
</property>
<property name="zipcode" display-name-key="Zip Code" description-key="Zip Code" of-type="SingleLine.Text" usage="bound" required="true" />
<property name="country" display-name-key="Country" description-key="Country" of-type-group="StringOrOptionset" usage="bound" required="false" />
<property name="countryvisible" usage="input" of-type="Enum" display-name-key="Country is Visible" description-key="Select if Country field is visible" required="true">
<value name="yes" display-name-key="Yes" description-key="Yes, Country is visible" default="true">yes</value>
<value name="no" display-name-key="No" description-key="No, Country is not visible">no</value>
</property>
<resources>
<code path="index.ts" order="1"/>
<css path="../node_modules/bootstrap/dist/css/bootstrap.min.css" order="1" />
<css path="css/CompositeAddress.css" order="2" />
</resources>
</control>
</manifest>
40 changes: 40 additions & 0 deletions CompositeAddress/css/CompositeAddress.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.compositeAddress {
background: transparent;
border-style: none;
color: #000000;
font-size: 1.0rem;
font-weight: 600;
height: 2.5rem;
line-height: 2.5rem;
width: 98%;
padding-left: 0.5em;
padding-right: 0.5em;
outline: 0 !important;
}

.compositeAddressFocused {
background: transparent;
border-width: 1px;
border-color: #000000;
color: #000000;
font-size: 1.0rem;
height: 2.5rem;
line-height: 2.5rem;
width: 98%;
padding-left: 0.5em;
padding-right: 0.5em;
outline: 0 !important;
}

.d365StyleInput {
background: transparent;
border-width: 1px;
border-color: #000000;
color: #000000;
font-size: 1.0rem;
height: 2.5rem;
line-height: 2.5rem;
padding-left: 0.5em;
padding-right: 0.5em;
outline: 0 !important;
}
220 changes: 220 additions & 0 deletions CompositeAddress/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { IInputs, IOutputs } from "./generated/ManifestTypes";
import * as $ from "jquery";
import * as Bootstrap from "bootstrap";

export class CompositeAddress implements ComponentFramework.StandardControl<IInputs, IOutputs> {

private notifyOutputChanged: () => void;
private mainAddressField: HTMLInputElement;
private fullAddressFormat: string;
private currentValues: any = new Object();
private currentLabels: any = new Object();
private allControls: any = new Object();

constructor() {

}

public init(context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement) {
Bootstrap.hasOwnProperty("test");

this.notifyOutputChanged = notifyOutputChanged;

this.fullAddressFormat = context.parameters.format.raw;

this.mainAddressField = document.createElement("input");
this.mainAddressField.className = "compositeAddress";
this.mainAddressField.readOnly = true;

if (context.parameters.value.raw !== null) {
this.mainAddressField.value = context.parameters.value.raw.replace(/\n/gm, ', ');
} else {
this.mainAddressField.value = "---";
}

this.mainAddressField.addEventListener("mouseenter", () => {
this.mainAddressField.className = "compositeAddressFocused";
});
this.mainAddressField.addEventListener("mouseleave", () => {
this.mainAddressField.className = "compositeAddress";
});

container.appendChild(this.mainAddressField);

let popupDiv = document.createElement("div");

this.addControltoPopup(context.parameters.street1, popupDiv, "street1");

if (context.parameters.street2visible.raw === "yes") {
this.addControltoPopup(context.parameters.street2, popupDiv, "street2");
}

if (context.parameters.street3visible.raw === "yes") {
this.addControltoPopup(context.parameters.street3, popupDiv, "street3");
}

this.addControltoPopup(context.parameters.city, popupDiv, "city");

if (context.parameters.countyvisible.raw === "yes") {
this.addControltoPopup(context.parameters.county, popupDiv, "county");
}

if (context.parameters.statevisible.raw === "yes") {
this.addControltoPopup(context.parameters.state, popupDiv, "state");
}

this.addControltoPopup(context.parameters.zipcode, popupDiv, "zipcode");

if (context.parameters.countryvisible.raw === "yes") {
this.addControltoPopup(context.parameters.country, popupDiv, "country");
}

$(this.mainAddressField).popover({
content: popupDiv,
html: true,
placement: "bottom"
});
}

private addControltoPopup(addressProperty: ComponentFramework.PropertyTypes.Property,
container: HTMLDivElement,
controlId: string): void {
let rowDiv = document.createElement("div");
rowDiv.className = "form-group";
container.appendChild(rowDiv);

if (addressProperty.type === "SingleLine.Text") {
let stringAddressProperty = addressProperty as ComponentFramework.PropertyTypes.StringProperty;

let fieldLabel = document.createElement("label");
fieldLabel.innerText = stringAddressProperty.attributes === undefined ? "" : stringAddressProperty.attributes.DisplayName;
rowDiv.appendChild(fieldLabel);

let inputControl = document.createElement("input");
inputControl.id = controlId;
inputControl.className = "form-control d365StyleInput";
inputControl.setAttribute("placeholder", "---");
inputControl.value = stringAddressProperty.raw;
inputControl.addEventListener("change", () => {
let currentText = inputControl.value;

this.currentValues[controlId] = currentText;
this.currentLabels[controlId] = currentText === null ? "" : currentText;

this.formatAddress();

this.notifyOutputChanged();
});

this.currentValues[controlId] = stringAddressProperty.raw;
this.currentLabels[controlId] = stringAddressProperty.formatted;
this.allControls[controlId] = inputControl;

rowDiv.appendChild(inputControl);
} else if (addressProperty.type === "OptionSet") {
let optionsetAddressProperty = addressProperty as ComponentFramework.PropertyTypes.OptionSetProperty;

let fieldLabel = document.createElement("label");
fieldLabel.innerText = optionsetAddressProperty.attributes === undefined
? ""
: optionsetAddressProperty.attributes.DisplayName;
rowDiv.appendChild(fieldLabel);

let selectControl = document.createElement("select");
selectControl.id = controlId;
selectControl.className = "form-control d365StyleInput";

let option: HTMLOptionElement = document.createElement("option");
option.innerHTML = "---Select---";
selectControl.add(option);

if (optionsetAddressProperty.attributes !== undefined) {
optionsetAddressProperty.attributes.Options.forEach(optionRecord => {
option = document.createElement("option");
option.innerHTML = optionRecord.Label;
option.value = optionRecord.Value.toString();

if (optionsetAddressProperty.raw === optionRecord.Value) {
option.selected = true;
}

selectControl.add(option);
});
}

selectControl.addEventListener("change", () => {
if (selectControl.value === "") {
this.currentValues[controlId] = null;
this.currentLabels[controlId] = "";
} else {
this.currentValues[controlId] = parseInt(selectControl.value);
this.currentLabels[controlId] = selectControl.options[selectControl.selectedIndex].label;
}

this.formatAddress();

this.notifyOutputChanged();
});

this.currentValues[controlId] = optionsetAddressProperty.raw;
this.currentLabels[controlId] = optionsetAddressProperty.formatted;
this.allControls[controlId] = selectControl;

rowDiv.appendChild(selectControl);
} else {
throw new Error(`Composite Address: can't render control for ${addressProperty.type} type of field`);
}
}

private formatAddress(): void {
let formattedAddress = this.fullAddressFormat;

formattedAddress = formattedAddress.replace(/\\n/gm, '\n');

for (let propName in this.currentLabels) {
let propertyValue = this.currentLabels[propName] === null ? "" : this.currentLabels[propName];

formattedAddress = formattedAddress.replace(`{${propName}}`, propertyValue);
}

this.currentValues.value = formattedAddress;
this.mainAddressField.value = formattedAddress.replace(/\n/gm, ', ');
}

public updateView(context: ComponentFramework.Context<IInputs>): void {
let formatChanged: boolean = false;

context.updatedProperties.forEach(updatedProperty => {
if (!context.parameters.hasOwnProperty(updatedProperty)) {
return ;
}

formatChanged = true;

let updatedPropertyObject = (context.parameters as any)[updatedProperty];

if (this.allControls.hasOwnProperty(updatedProperty)) {
$(this.allControls[updatedProperty]).val(updatedPropertyObject.raw);
}

this.currentValues[updatedProperty] = updatedPropertyObject.raw;
this.currentLabels[updatedProperty] = updatedPropertyObject.formatted;
});

if (formatChanged) {
this.formatAddress();
this.notifyOutputChanged();
}
}

public getOutputs(): IOutputs {
return this.currentValues as IOutputs;
}

public destroy(): void {
// Add code to cleanup control if necessary
}
}
2 changes: 2 additions & 0 deletions CompositeAddressSolution/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin
/obj
48 changes: 48 additions & 0 deletions CompositeAddressSolution/CompositeAddressSolution.cdsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PowerAppsTargetsPath>$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\PowerApps</PowerAppsTargetsPath>
</PropertyGroup>

<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.props" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.props')" />

<PropertyGroup>
<ProjectGuid>3ecce81b-4fc4-4969-8051-d95b273ab784</ProjectGuid>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<!--Remove TargetFramework when this is available in 16.1-->
<TargetFramework>net462</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>

<!-- Solution Packager overrides, un-comment to use: SolutionPackagerType (Managed, Unmanaged, Both)
<PropertyGroup>
<SolutionPackageType>Managed</SolutionPackageType>
</PropertyGroup>
-->

<ItemGroup>
<PackageReference Include="Microsoft.PowerApps.MSBuild.Solution" Version="0.*" />
</ItemGroup>

<ItemGroup>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\.gitignore" />
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\bin\**" />
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\obj\**" />
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.cdsproj" />
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.cdsproj.user" />
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.sln" />
</ItemGroup>

<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\**" Exclude="@(ExcludeDirectories)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CompositeAddress_PCF.pcfproj" />
</ItemGroup>

<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.targets" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Solution.targets')" />

</Project>
17 changes: 17 additions & 0 deletions CompositeAddressSolution/Other/Customizations.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<ImportExportXml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Entities />
<Roles />
<Workflows />
<FieldSecurityProfiles />
<Templates />
<EntityMaps />
<EntityRelationships />
<OrganizationSettings />
<optionsets />
<CustomControls />
<EntityDataProviders />
<Languages>
<Language>1033</Language>
</Languages>
</ImportExportXml>
Loading

0 comments on commit 5d3439c

Please sign in to comment.