Skip to content

Commit 15e02be

Browse files
committed
Initial commit (v 0.0.2)
0 parents  commit 15e02be

17 files changed

Lines changed: 8768 additions & 0 deletions

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
# msbuild output directories
13+
/bin
14+
/obj
15+
16+
# solution directory
17+
/WebFormStepsVisualizerSolution

PCF-WebFormStepVisualizer.pcfproj

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.Pcf.props" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.props')"/>
9+
10+
<PropertyGroup>
11+
<Name>PCF-WebFormStepVisualizer</Name>
12+
<ProjectGuid>84f0f4c3-4d6f-4054-b72d-9622a3929b36</ProjectGuid>
13+
<OutputPath>$(MSBuildThisFileDirectory)out\controls</OutputPath>
14+
</PropertyGroup>
15+
16+
<PropertyGroup>
17+
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
18+
<!--Remove TargetFramework when this is available in 16.1-->
19+
<TargetFramework>net462</TargetFramework>
20+
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
21+
</PropertyGroup>
22+
23+
<ItemGroup>
24+
<PackageReference Include="Microsoft.PowerApps.MSBuild.Pcf" Version="1.*"/>
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\.gitignore"/>
29+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\bin\**"/>
30+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\obj\**"/>
31+
<ExcludeDirectories Include="$(OutputPath)\**"/>
32+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.pcfproj"/>
33+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.pcfproj.user"/>
34+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.sln"/>
35+
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\node_modules\**"/>
36+
</ItemGroup>
37+
38+
<ItemGroup>
39+
<None Include="$(MSBuildThisFileDirectory)\**" Exclude="@(ExcludeDirectories)"/>
40+
</ItemGroup>
41+
42+
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
43+
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.targets" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.targets')"/>
44+
45+
</Project>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import * as React from "react";
2+
3+
import { initializeIcons } from "@uifabric/icons";
4+
initializeIcons();
5+
6+
import { IChart, FlowChartWithState } from "@mrblenny/react-flow-chart";
7+
import { Page } from "./Page";
8+
import styled from "styled-components";
9+
import { ChartContext } from "./ChartContext";
10+
import { NodeInnerCustom } from "./NodeInnerCustom";
11+
import { Sidebar, ISelectedEntityProps } from "./Sidebar";
12+
13+
const Content = styled.div`
14+
display: flex;
15+
flex-direction: column;
16+
flex: 1;
17+
overflow: hidden;
18+
`;
19+
20+
export interface IAppProps {
21+
initialChart: IChart;
22+
openRecord: Function;
23+
}
24+
25+
export const App = (props: IAppProps) => {
26+
const [selected, setSelected] = React.useState<Partial<ISelectedEntityProps>>(
27+
{}
28+
);
29+
30+
console.log("App selected", selected);
31+
32+
return (
33+
<Page>
34+
<ChartContext.Provider value={{ setSelected: setSelected }}>
35+
<Content>
36+
<FlowChartWithState
37+
initialValue={props.initialChart}
38+
Components={{
39+
NodeInner: NodeInnerCustom,
40+
}}
41+
/>
42+
</Content>
43+
</ChartContext.Provider>
44+
<Sidebar openRecord={props.openRecord} selected={selected}></Sidebar>
45+
</Page>
46+
);
47+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from "react"
2+
import { INode } from "@mrblenny/react-flow-chart";
3+
4+
export interface IChartContextProps {
5+
setSelected: Function
6+
}
7+
8+
export const ChartContext = React.createContext<Partial<IChartContextProps>>(
9+
{}
10+
);;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as React from "react";
2+
import styled from "styled-components";
3+
import { INodeDefaultProps } from "@mrblenny/react-flow-chart";
4+
5+
const DarkBox = styled.div`
6+
position: absolute;
7+
padding: 30px;
8+
background: #3e3e3e;
9+
color: white;
10+
border-radius: 10px;
11+
`;
12+
13+
const Circle = styled.div`
14+
position: absolute;
15+
width: 150px;
16+
height: 150px;
17+
padding: 30px;
18+
display: flex;
19+
justify-content: center;
20+
align-items: center;
21+
background: #d30000;
22+
color: white;
23+
border-radius: 50%;
24+
`;
25+
26+
const ConditionNode = styled.div`
27+
background-color: #dadada;
28+
width: 200px;
29+
height: 200px;
30+
display: block;
31+
word-break: break-all;
32+
transform: rotate(45deg);
33+
`;
34+
35+
/**
36+
* Create the custom component,
37+
* Make sure it has the same prop signature
38+
* You'll need to add {...otherProps} so the event listeners are added to your component
39+
*/
40+
export const CustomNode = React.forwardRef(
41+
(
42+
{ node, children, ...otherProps }: INodeDefaultProps,
43+
ref: React.Ref<HTMLDivElement>
44+
) => {
45+
if (node.type === "custom") {
46+
return (
47+
<ConditionNode ref={ref} {...otherProps}>
48+
{children}
49+
</ConditionNode>
50+
);
51+
} else {
52+
return (
53+
<DarkBox ref={ref} {...otherProps}>
54+
{children}
55+
</DarkBox>
56+
);
57+
}
58+
}
59+
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as React from "react";
2+
import styled from "styled-components";
3+
import {
4+
INodeInnerDefaultProps,
5+
} from "@mrblenny/react-flow-chart";
6+
7+
import { ChartContext } from "./ChartContext";
8+
import { ISelectedEntityProps } from "./Sidebar";
9+
10+
const Outer = styled.div`
11+
padding: 30px;
12+
`;
13+
14+
export const NodeInnerCustom = ({ node, config }: INodeInnerDefaultProps) => {
15+
const { setSelected } = React.useContext(ChartContext);
16+
17+
const handleClick = () => {
18+
if (setSelected) {
19+
let newSelectedEntityProps = {...node.properties} as ISelectedEntityProps;
20+
console.log("Handle CLick", newSelectedEntityProps);
21+
setSelected(newSelectedEntityProps);
22+
}
23+
};
24+
25+
let condition;
26+
27+
if (node.properties.condition) {
28+
condition = <p>{node.properties.condition}</p>;
29+
}
30+
31+
return (
32+
<Outer onClick={handleClick}>
33+
<p>{node.properties.title}</p>
34+
{condition}
35+
</Outer>
36+
);
37+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as React from 'react'
2+
import styled, { createGlobalStyle } from 'styled-components'
3+
4+
const GlobalStyle = createGlobalStyle`
5+
body {
6+
margin: 0px;
7+
max-width: 100vw;
8+
max-height: 100vh;
9+
overflow: hidden;
10+
box-sizing: border-box;
11+
font-family: sans-serif;
12+
}
13+
14+
*, :after, :before {
15+
box-sizing: inherit;
16+
}
17+
`
18+
19+
const PageContent = styled.div`
20+
display: flex;
21+
flex-direction: row;
22+
flex: 1;
23+
width:80vw;
24+
max-width: 100vw;
25+
max-height: 100vh;
26+
height: 800px;
27+
`
28+
29+
export const Page = ({ children }: { children: any}) => (
30+
<PageContent>
31+
{children}
32+
<GlobalStyle />
33+
</PageContent>
34+
)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as React from "react";
2+
import styled from "styled-components";
3+
4+
import {
5+
Stack,
6+
TextField,
7+
Dropdown,
8+
IDropdownOption,
9+
PrimaryButton,
10+
IStackTokens,
11+
} from "@fluentui/react";
12+
13+
import { WebFormStepType, WebFormStepMode } from "../utils/WebFormInterfaces";
14+
15+
const SidebarWrapper = styled.div`
16+
width: 300px;
17+
background: white;
18+
display: flex;
19+
flex-direction: column;
20+
flex-shrink: 0;
21+
text-align: start;
22+
`;
23+
24+
const Message = styled.div`
25+
margin: 10px;
26+
padding: 10px;
27+
line-height: 1.4em;
28+
`;
29+
30+
const verticalGapStackTokens: IStackTokens = {
31+
childrenGap: 10,
32+
padding: 10,
33+
};
34+
35+
const typeOptions: IDropdownOption[] = [
36+
{ key: WebFormStepType.Condition, text: "Condtion" },
37+
{ key: WebFormStepType.LoadForm, text: "Load Form" },
38+
{ key: WebFormStepType.LoadTab, text: "Load Tab" },
39+
{ key: WebFormStepType.LoadUserControl, text: "Load User Control" },
40+
{ key: WebFormStepType.Redirect, text: "Redirect" },
41+
];
42+
43+
const modeOptions: IDropdownOption[] = [
44+
{ key: WebFormStepMode.Insert, text: "Insert" },
45+
{ key: WebFormStepMode.Edit, text: "Edit" },
46+
{ key: WebFormStepMode.ReadOnly, text: "Read Only" },
47+
];
48+
49+
export interface ISelectedEntityProps {
50+
title: string;
51+
entity: string;
52+
type: WebFormStepType;
53+
mode: WebFormStepMode;
54+
id: string;
55+
condition:string;
56+
}
57+
58+
interface ISidebarProps {
59+
selected: Partial<ISelectedEntityProps>;
60+
openRecord: Function;
61+
}
62+
63+
export const Sidebar = (props: ISidebarProps) => {
64+
const selected = props.selected;
65+
console.log("selected", selected);
66+
67+
return (
68+
<SidebarWrapper>
69+
{selected !== undefined && selected.type ? (
70+
<Stack tokens={verticalGapStackTokens}>
71+
<TextField label="Name" value={selected.title} />
72+
<TextField label="Entity" value={selected.entity} />
73+
<Dropdown
74+
label="Type"
75+
selectedKey={selected.type}
76+
options={typeOptions}
77+
/>
78+
{selected.type == WebFormStepType.Condition ? (<TextField label="Condition" value={selected.condition} />): null}
79+
<Dropdown
80+
label="Mode"
81+
selectedKey={selected.mode}
82+
options={modeOptions}
83+
disabled={selected.mode == null}
84+
/>
85+
<PrimaryButton
86+
iconProps={{ iconName: "OpenInNewWindow" }}
87+
text="Open Record"
88+
onClick={() => props.openRecord(selected.id)}
89+
/>
90+
</Stack>
91+
) : (
92+
<Message>Click on a Node</Message>
93+
)}
94+
</SidebarWrapper>
95+
);
96+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<manifest>
3+
<control namespace="dancingwithcrm" constructor="WebFormStepsVisualizer" version="0.2.0" display-name-key="WebFormStepsVisualizer" description-key="WebFormStepsVisualizer description" control-type="standard">
4+
<!-- property node identifies a specific, configurable piece of data that the control expects from CDS -->
5+
<property name="placeHolder" display-name-key="Placeholder field" description-key="This field will be used as a placeholder only" of-type="SingleLine.Text" usage="bound" required="true" />
6+
7+
<resources>
8+
<code path="index.ts" order="1"/>
9+
</resources>
10+
<feature-usage>
11+
<uses-feature name="Utility" required="true" />
12+
<uses-feature name="WebAPI" required="true" />
13+
</feature-usage>
14+
15+
</control>
16+
</manifest>

0 commit comments

Comments
 (0)