Skip to content

Commit

Permalink
more work on the settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
Arlen22 committed Mar 16, 2018
1 parent 059b303 commit f60d1bd
Show file tree
Hide file tree
Showing 24 changed files with 1,570 additions and 992 deletions.
Expand Up @@ -10,7 +10,7 @@
<!-- https: //code.angularjs.org/1.6.9 -->
<script src="/static/polyfill-promise.js"></script>
<script src="/static/angular.min.js"></script>
<script src="/static/settings-page.js"></script>
<script src="/static/settings-root.js"></script>
<title></title>
</head>

Expand Down
199 changes: 199 additions & 0 deletions assets/settings-root/settings-root.ts
@@ -0,0 +1,199 @@

type Hashmap<T> = { [K: string]: T };
type primitive = "string" | "number" | "boolean";
type SettingsPageItem = {
level: 0 | 1 | 2,
name: string,
// valueType: string,
// valueOptions?: any[]
};
type ValueType_function = {
valueType: "function",
// validate: (level: number, upd: ServerConfig) => { valid: boolean, value: any, isChanged: boolean }
} & SettingsPageItem;
type ValueType_primitive = {
valueType: primitive
} & SettingsPageItem;
type ValueType_enum = {
valueType: "enum",
enumType: primitive,
enumOpts: any[]
// valueOptions: ["number" | "string", (number | string)[]]
} & SettingsPageItem;
type ValueType_hashmapenum = {
valueType: "hashmapenum",
enumType: primitive,
enumKeys: string[]
} & SettingsPageItem;
type ValueType_subpage = {
valueType: "subpage",
// handler: (state: StateObject) => void;
} & SettingsPageItem;
type SettingsPageItemTypes = ValueType_function | ValueType_enum | ValueType_hashmapenum | ValueType_primitive | ValueType_subpage;

interface RootScope extends angular.IScope {

}

interface GlobalScope extends RootScope {

}



//@ts-ignore
let app = angular.module('settings', [

]);
app.config(function ($locationProvider) {
$locationProvider.html5Mode(false).hashPrefix('*');
});
app.run(function ($templateCache) {
var templates = {
string: `<input type="text" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`,
number: `<input type="number" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`,
boolean: `<input type="checkbox" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`,
enum: `
<select name="{{item.name}}" value="" ng-disabled="readonly" title="{{item.name}}"
ng-model="outputs[item.name]"
ng-options="k for k in item.enumOpts">
</select> <span ng-bind-html="description"></span>
`,
hashmapenum: `
<div ng-repeat="(i, key) in item.enumKeys track by $index"
ng-controller="HashmapEnumItemCtrl"
ng-include="'template-' + item.valueType"></div>
`,
subpage: `<a href="{{item.name}}">Please access this setting at the {{item.name}} subpage.</a>`,
function: `
<ng-include src="'template-function' + item.name"></ng-include>
`,
functiontypes: `Coming soon`,
settingsPage: `
<fieldset ng-repeat="(i, item) in data" ng-controller="SettingsPageItemCtrl">
<legend>{{item.name}}</legend>
<div ng-include="'template-' + item.valueType"></div>
</fieldset>
`
}

for (var i in templates) {
$templateCache.put("template-" + i, templates[i]);
}
});
interface HashmapEnumItemCtrlScope extends SettingsPageItemCtrlScope {
key: string;
}
app.controller("HashmapEnumItemCtrl", function ($scope: HashmapEnumItemCtrlScope, $sce: angular.ISCEService) {
let parentItem: SettingsPageItemTypes = $scope.item;
if (parentItem.valueType !== "hashmapenum") return;
$scope.item = {
valueType: parentItem.enumType,
name: $scope.key,
level: parentItem.level
} as SettingsPageItemTypes;
if (typeof $scope.outputs[parentItem.name] !== "object")
$scope.outputs[parentItem.name] = {};
$scope.outputs = $scope.outputs[parentItem.name];
$scope.description = $scope.description[$scope.key];
if (typeof $scope.description === "string") {
$scope.description = $sce.trustAsHtml($scope.description);
}
});
interface SettingsPageItemCtrlScope extends SettingsPageCtrlScope {
item: SettingsPageItemTypes;
description: string | Hashmap<any>;
readonly: boolean;
}
app.controller("SettingsPageItemCtrl", function ($scope: SettingsPageItemCtrlScope, $sce: angular.ISCEService) {

$scope.description = $scope.descriptions[$scope.item.name];
if (typeof $scope.description === "string") {
$scope.description = $sce.trustAsHtml($scope.description);
}
$scope.readonly = $scope.item.level > $scope.level;

});
interface SettingsPageCtrlScope extends RootScope {
data: SettingsPageItemTypes[];
descriptions: Hashmap<string | Hashmap<any>>;
outputs: Hashmap<any>;
level: number;
}
app.controller("SettingsPageCtrl", function ($scope: SettingsPageCtrlScope, $http: angular.IHttpService) {
let timeout: number;
let saveWait = 1000;
let oldSettings: Hashmap<string>;
function saveSettings() {
let set = {};
$scope.data.forEach(item => {
let key = item.name;
let newval = JSON.stringify($scope.outputs[key]);
console.log(newval, oldSettings[key]);
if (oldSettings[key] !== newval) {
if (item.level <= $scope.level)
set[key] = $scope.outputs[key];
oldSettings[key] = newval;
}
})
$http.put("?action=update", JSON.stringify(set));
}

$http.get('?action=getdata').then(res => {
let { level, data, descriptions, settings } = res.data as any;
let timeout;

$scope.$watch((s: SettingsPageCtrlScope) => JSON.stringify(s.outputs), (item, old) => {
if (!old || item === old) return;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(saveSettings, saveWait);
});

$scope.data = data;
$scope.descriptions = descriptions;
$scope.outputs = settings;
$scope.level = level;
oldSettings = {};
data.forEach(item => {
oldSettings[item.name] = JSON.stringify(settings[item.name]);
})
$scope.$broadcast('refresh');
});
})





// angular.module("settings", [

// ]).controller("globalCtrl", function ($scope: GlobalScope, $http: angular.IHttpService) {
// $http.get("?")
// })

// const old = {
// "template-children":/* just some green */ `
// <div ng-repeat="(i, item) in item.children track by $index"
// ng-class="[item.classes, 'type-' + item.type, 'output-' + item.output].join(' ')"
// ng-include="'template-' + item.type"></div>`,
// "template-repeater":/* just some green */ `
// <div ng-repeat="(j, outputs) in outputs[item.output]"
// ng-class="item.repeaterClasses"
// ng-include="'template-children'"></div>`,
// "template-hashmap-item":/* just some green */ `
// <li ng-repeat="(j, outputs) in outputs">
// <code style="min-width: 200px;">{{j}}</code>
// <ul ng-if="typeof(outputs) == 'object'" ng-include="'template-hashmap-item'"></ul>
// <code ng-if="typeof(outputs) == 'string'">{{outputs}}</code>
// </li>`,
// 'template-hashmap':/* just some green */ `
// <ul ng-include="'template-hashmap-item'" ></ul>
// <input type="text" ng-model="outputs[item.output]['$add']" />
// <button ng-click="outputs[item.output][outputs[item.output]['$add']] = ''">+ Item</button>
// <button ng-click="outputs[item.output][outputs[item.output]['$add']] = {}">+ Hash</button>
// `,
// 'template-text-row':/* just some green */ `
// <span style="display:inline-block; width:200px">{{item.name}}</span>
// <input type="text" ng-model="outputs[item.output]"/>
// `
// }
5 changes: 5 additions & 0 deletions assets/settings-root/tsconfig.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"outDir": "../static/"
}
}
22 changes: 22 additions & 0 deletions assets/settings-tree/settings-tree.html
@@ -0,0 +1,22 @@
<!doctype html>
<html>

<head>
<style>
dl.treelist {
margin: 0;
}
</style>
<!-- https: //code.angularjs.org/1.6.9 -->
<script src="/static/polyfill-promise.js"></script>
<script src="/static/angular.min.js"></script>
<script src="/static/settings-tree.js"></script>
<title></title>
</head>

<body ng-app="settings">
<h1>Hi</h1>
<div ng-include="'template-settingsPage'" ng-controller="SettingsPageCtrl"></div>
</body>

</html>
132 changes: 132 additions & 0 deletions assets/settings-tree/settings-tree.ts
@@ -0,0 +1,132 @@

type Hashmap<T> = { [K: string]: T };
type SettingsPageItem = {
level: 0 | 1 | 2,
name: string,
// valueType: string,
// valueOptions?: any[]
};
type ValueType_function = {
valueType: "function",
// valueOptions: [(defValue: any, keys: string[], readOnly: boolean, description: any) => string]
} & SettingsPageItem;
type ValueType_primitive = {
valueType: "string" | "number" | "boolean"
} & SettingsPageItem;
type ValueType_enum = {
valueType: "enum",
valueOptions: ["number" | "string", (number | string)[]]
} & SettingsPageItem;
type ValueType_hashmapenum = {
valueType: "hashmapenum",
valueOptions: [("string" | "number" | "boolean")[], string[]]
} & SettingsPageItem;
type ValueType_subpage = {
valueType: "subpage",
valueOptions: {}
} & SettingsPageItem;
type SettingsPageItemTypes = ValueType_function | ValueType_enum | ValueType_hashmapenum | ValueType_primitive | ValueType_subpage;

interface RootScope extends angular.IScope {

}

interface GlobalScope extends RootScope {

}



//@ts-ignore
let app = angular.module('settings', [

]);
app.config(function ($locationProvider) {
$locationProvider.html5Mode(false).hashPrefix('*');
});
app.run(function ($templateCache) {
var templates = {
string: `<input type="text" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`,
number: `<input type="number" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`,
boolean: `<input type="checkbox" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`,
enum: `
<select name="{{item.name}}" value="" ng-disabled="readonly" title="{{item.name}}"
ng-model="outputs[item.name]"
ng-options="k for k in item.valueOptions[1]">
</select> <span ng-bind-html="description"></span>
`,
hashmapenum: `
<div ng-repeat="(i, key) in item.valueOptions[1] track by $index"
ng-controller="HashmapEnumItemCtrl"
ng-include="'template-' + item.valueType"></div>
`,
subpage: `<a href="{{item.name}}">Please access this setting at the {{item.name}} subpage.</a>`,
function: `
<ng-include src="'template-function' + item.name"></ng-include>
`,
functiontypes: `Coming soon`,
settingsPage: `
<fieldset ng-repeat="(i, item) in data" ng-controller="SettingsPageItemCtrl">
<legend>{{item.name}}</legend>
<div ng-include="'template-' + item.valueType"></div>
</fieldset>
`
}

for (var i in templates) {
$templateCache.put("template-" + i, templates[i]);
}
});
interface HashmapEnumItemCtrlScope extends SettingsPageItemCtrlScope {
key: string;
}
app.controller("HashmapEnumItemCtrl", function ($scope: HashmapEnumItemCtrlScope, $sce: angular.ISCEService) {
let parentItem: SettingsPageItemTypes = $scope.item;
if (parentItem.valueType !== "hashmapenum") return;
$scope.item = {
valueType: parentItem.valueOptions[0][0],
name: $scope.key,
level: parentItem.level
} as SettingsPageItemTypes;
if (typeof $scope.outputs[parentItem.name] !== "object")
$scope.outputs[parentItem.name] = {};
$scope.outputs = $scope.outputs[parentItem.name];
$scope.description = $scope.description[$scope.key];
if (typeof $scope.description === "string") {
$scope.description = $sce.trustAsHtml($scope.description);
}
});
interface SettingsPageItemCtrlScope extends SettingsPageCtrlScope {
item: SettingsPageItemTypes;
description: string | Hashmap<any>;
readonly: boolean;
}
app.controller("SettingsPageItemCtrl", function ($scope: SettingsPageItemCtrlScope, $sce: angular.ISCEService) {

$scope.description = $scope.descriptions[$scope.item.name];
if (typeof $scope.description === "string") {
$scope.description = $sce.trustAsHtml($scope.description);
}
$scope.readonly = $scope.item.level > $scope.level;

});
interface SettingsPageCtrlScope extends RootScope {
data: SettingsPageItemTypes[];
descriptions: Hashmap<string | Hashmap<any>>;
outputs: Hashmap<any>;
level: number;
}
app.controller("SettingsPageCtrl", function ($scope: SettingsPageCtrlScope, $http: angular.IHttpService) {
$http.get('?action=getdata').then(res => {
let { level, data, descriptions, settings } = res.data as any;
$scope.data = data;
$scope.descriptions = descriptions;
$scope.outputs = settings;
$scope.level = level;
$scope.$broadcast('refresh');
});
})




5 changes: 5 additions & 0 deletions assets/settings-tree/tsconfig.json
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"outDir": "../static/"
}
}

0 comments on commit f60d1bd

Please sign in to comment.