mirror of
https://github.com/lxsang/antos-frontend.git
synced 2024-12-25 17:08:21 +01:00
feat: add APIs that support responsive UI on antos tags
This commit is contained in:
parent
a5257bf108
commit
e6bf4d5352
@ -144,6 +144,33 @@ namespace OS {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* API function to register responsive UI event to the current window tag
|
||||
*
|
||||
* @protected
|
||||
* @param {GUI.TagResponsiveValidator} responsive validator
|
||||
* @param {GUI.TagResponsiveCallback} responsive callback
|
||||
* @returns {void}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected morphon(validator: GUI.TagResponsiveValidator, callback: GUI.TagResponsiveCallback)
|
||||
{
|
||||
const win = this.scheme as GUI.tag.WindowTag;
|
||||
win.morphon(validator, callback);
|
||||
}
|
||||
/**
|
||||
* API function to unregister responsive UI event from current window tag
|
||||
*
|
||||
* @protected
|
||||
* @param {GUI.TagResponsiveValidator} responsive validator
|
||||
* @returns {void}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected morphoff(validator: GUI.TagResponsiveValidator)
|
||||
{
|
||||
const win = this.scheme as GUI.tag.WindowTag;
|
||||
win.morphoff(validator);
|
||||
}
|
||||
/**
|
||||
* Render the application UI by first loading its scheme
|
||||
* and then mount this scheme to the DOM tree
|
||||
|
@ -701,12 +701,16 @@ namespace OS {
|
||||
* @returns {HTMLElement}
|
||||
* @memberof BaseModel
|
||||
*/
|
||||
protected find(id: string): HTMLElement {
|
||||
protected find<T extends HTMLElement>(id: string): T {
|
||||
if (this.scheme) {
|
||||
return $(`[data-id='${id}']`, this.scheme)[0];
|
||||
return $(`[data-id='${id}']`, this.scheme)[0] as T;
|
||||
}
|
||||
}
|
||||
|
||||
/*protected $(id: string) : T {
|
||||
return this.find(id) as T;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Select all DOM Element inside the UI of the model
|
||||
* using JQuery selector
|
||||
|
@ -101,6 +101,7 @@ namespace OS {
|
||||
* @memberof FloatListTag
|
||||
*/
|
||||
protected mount(): void {
|
||||
$(this.refs.current).hide();
|
||||
$(this.refs.container)
|
||||
.css("width", "100%")
|
||||
.css("height", "100%");
|
||||
|
@ -1248,6 +1248,7 @@ namespace OS {
|
||||
{
|
||||
el: "div",
|
||||
ref: "container",
|
||||
class: "grid_content_container",
|
||||
children: [{ el: "div", ref: "grid" }],
|
||||
},
|
||||
];
|
||||
|
@ -548,6 +548,9 @@ namespace OS {
|
||||
*/
|
||||
private _data: GenericObject<any>[];
|
||||
|
||||
private _drop: (any) => void;
|
||||
private _show: (any) => void;
|
||||
|
||||
/**
|
||||
* Event data passing between mouse event when performing
|
||||
* drag and drop on the list
|
||||
@ -577,6 +580,8 @@ namespace OS {
|
||||
) => {};
|
||||
this._selectedItems = [];
|
||||
this._selectedItem = undefined;
|
||||
this._drop = (e) => {this.dropoff(e)};
|
||||
this._show = (e) => {this.showlist(e)};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -617,23 +622,18 @@ namespace OS {
|
||||
$(this.refs.container).removeAttr("style");
|
||||
$(this.refs.mlist).removeAttr("style");
|
||||
$(this).removeClass("dropdown");
|
||||
const drop = (e: any) => {
|
||||
return this.dropoff(e);
|
||||
};
|
||||
const show = (e: any) => {
|
||||
return this.showlist(e);
|
||||
};
|
||||
if (v) {
|
||||
$(this).addClass("dropdown");
|
||||
$(this.refs.current).show();
|
||||
$(document).on("click", drop);
|
||||
$(this.refs.current).on("click", show);
|
||||
$(document).on("click", this._drop);
|
||||
$(this.refs.current).on("click", this._show);
|
||||
$(this.refs.mlist).hide();
|
||||
this.calibrate();
|
||||
} else {
|
||||
$(document).off("click", this._drop);
|
||||
$(this.refs.current).off("click", this._show);
|
||||
$(this.refs.current).hide();
|
||||
$(document).off("click", drop);
|
||||
$(this.refs.current).off("click", show);
|
||||
$(this.refs.mlist).show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1157,10 +1157,11 @@ namespace OS {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set the label content event it is hidden
|
||||
const label = this.refs.drlabel as LabelTag;
|
||||
label.set(e.data.data);
|
||||
if (this.dropdown) {
|
||||
const label = this.refs.drlabel as LabelTag;
|
||||
label.set(e.data.data);
|
||||
$(this.refs.mlist).hide();
|
||||
}
|
||||
const evt = { id: this.aid, data: edata };
|
||||
@ -1350,23 +1351,37 @@ namespace OS {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the list view to bottom
|
||||
* Scroll the list view to end
|
||||
*
|
||||
* @memberof ListViewTag
|
||||
*/
|
||||
scroll_to_bottom()
|
||||
scroll_to_end()
|
||||
{
|
||||
this.refs.mlist.scrollTo({ top: this.refs.mlist.scrollHeight, behavior: 'smooth' })
|
||||
if(this.dir == "vertical")
|
||||
{
|
||||
this.refs.mlist.scrollTo({ top: this.refs.mlist.scrollHeight, behavior: 'smooth' });
|
||||
}
|
||||
else
|
||||
{
|
||||
this.refs.mlist.scrollTo({ left: this.refs.mlist.scrollWidth, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the list view to top
|
||||
* Scroll the list view to beginning
|
||||
*
|
||||
* @memberof ListViewTag
|
||||
*/
|
||||
scroll_to_top()
|
||||
scroll_to_start()
|
||||
{
|
||||
this.refs.mlist.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
if(this.dir == "vertical")
|
||||
{
|
||||
this.refs.mlist.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
else
|
||||
{
|
||||
this.refs.mlist.scrollTo({ left: 0, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,6 +136,7 @@ namespace OS {
|
||||
w: this.width,
|
||||
h: this.height,
|
||||
},
|
||||
root: true
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,10 @@ namespace OS {
|
||||
? $(this).prev()[0]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
if(this.dir)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (tagname === "AFX-HBOX") {
|
||||
this.dir = "hz";
|
||||
} else if (tagname === "AFX-VBOX") {
|
||||
@ -197,6 +200,20 @@ namespace OS {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter Disable or enable the resize event
|
||||
*
|
||||
* @memberof ResizerTag
|
||||
*/
|
||||
set disable(v: boolean)
|
||||
{
|
||||
this.attsw(v, "disable");
|
||||
}
|
||||
get disable(): boolean
|
||||
{
|
||||
return this.hasattr("disable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable draggable on the element
|
||||
*
|
||||
@ -210,6 +227,10 @@ namespace OS {
|
||||
}
|
||||
$(this).on("pointerdown", (e) => {
|
||||
e.preventDefault();
|
||||
if(this.disable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
$(window).on("pointermove", (evt) => {
|
||||
if (!this._resizable_el) {
|
||||
return;
|
||||
|
@ -257,6 +257,42 @@ namespace OS {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the tabbar to end
|
||||
*
|
||||
* @memberof TabBarTag
|
||||
*/
|
||||
scroll_to_end()
|
||||
{
|
||||
const list_container = $(".list-container", this.refs.list)[0];
|
||||
if(this.dir == "vertical")
|
||||
{
|
||||
list_container.scrollTo({ top: list_container.scrollHeight, behavior: 'smooth' });
|
||||
}
|
||||
else
|
||||
{
|
||||
list_container.scrollTo({ left: list_container.scrollWidth, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the tabbar to begin
|
||||
*
|
||||
* @memberof TabBarTag
|
||||
*/
|
||||
scroll_to_start()
|
||||
{
|
||||
const list_container = $(".list-container", this.refs.list)[0];
|
||||
if(this.dir == "vertical")
|
||||
{
|
||||
list_container.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
else
|
||||
{
|
||||
list_container.scrollTo({ left: 0, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TabBar layout definition
|
||||
*
|
||||
|
@ -1,6 +1,9 @@
|
||||
namespace OS {
|
||||
export namespace GUI {
|
||||
export namespace tag {
|
||||
|
||||
type TileItemDirection = "row" | "column" | "row-reverse" | "column-reverse";
|
||||
|
||||
/**
|
||||
* A tile layout organize it child elements
|
||||
* in a fixed horizontal or vertical direction.
|
||||
@ -77,15 +80,15 @@ namespace OS {
|
||||
*
|
||||
* @memberof TileLayoutTag
|
||||
*/
|
||||
set dir(v: "row" | "column") {
|
||||
set dir(v: TileItemDirection) {
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
$(this).attr("dir", v);
|
||||
$(this.refs.yield).css("flex-direction", v);
|
||||
this.reversed = this.reversed;
|
||||
this.calibrate();
|
||||
}
|
||||
get dir(): "row" | "column" {
|
||||
get dir(): TileItemDirection {
|
||||
return $(this).attr("dir") as any;
|
||||
}
|
||||
/**
|
||||
@ -108,6 +111,35 @@ namespace OS {
|
||||
{
|
||||
return this._padding;
|
||||
}
|
||||
/**
|
||||
* setter: Reverse order of the content in the tile
|
||||
*
|
||||
* getter: return if the tile's content is in reversed order
|
||||
*
|
||||
* @meberof TileLayoutTags
|
||||
*/
|
||||
set reversed(v: boolean)
|
||||
{
|
||||
this.attsw(v, "reversed");
|
||||
if(!this.dir)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let newdir = "row";
|
||||
if(this.dir.startsWith("column"))
|
||||
{
|
||||
newdir = "column"
|
||||
}
|
||||
if(v)
|
||||
{
|
||||
newdir += "-reverse";
|
||||
}
|
||||
$(this.refs.yield).css("flex-direction", newdir);
|
||||
}
|
||||
get reversed(): boolean
|
||||
{
|
||||
return this.hasattr("reversed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount the element
|
||||
|
@ -468,6 +468,7 @@ namespace OS {
|
||||
this.observable.trigger("resize", {
|
||||
id: this.aid,
|
||||
data: o,
|
||||
root: true
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -242,6 +242,13 @@ namespace OS {
|
||||
* @memberof TagEventType
|
||||
*/
|
||||
originalEvent?: any;
|
||||
/**
|
||||
* is root tag?
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof TagEventType
|
||||
*/
|
||||
root?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -270,8 +277,163 @@ namespace OS {
|
||||
}
|
||||
/**
|
||||
* Tag event callback type
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export type TagEventCallback<T> = (e: TagEventType<T>) => void;
|
||||
/**
|
||||
* Tag responsive envent callback type
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export type TagResponsiveCallback = (fullfilled: boolean) => void;
|
||||
|
||||
/**
|
||||
* A callback record with history of last fulfilled
|
||||
*
|
||||
* @interface TagResponsiveCallbackRecord
|
||||
*/
|
||||
interface TagResponsiveCallbackRecord {
|
||||
/**
|
||||
* Callback function
|
||||
*
|
||||
* @type {TagResponsiveCallback}
|
||||
* @memberof TagResponsiveCallbackRecord
|
||||
*/
|
||||
callback: TagResponsiveCallback,
|
||||
/**
|
||||
* has the event been previously fulfilled?
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof TagResponsiveCallbackRecord
|
||||
*/
|
||||
fulfilled: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag responsive validator type
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export type TagResponsiveValidator = (w: number, h: number) => boolean;
|
||||
|
||||
/**
|
||||
* Definitions of some default tag responsive validators
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
|
||||
export const RESPONSIVE = {
|
||||
/**
|
||||
* Extra small devices (phones, 600px and down)
|
||||
*/
|
||||
TINY: function(w: number,_h: number){
|
||||
return w <= 600;
|
||||
},
|
||||
/*
|
||||
Small devices (portrait tablets and large phones, 600px and up)
|
||||
*/
|
||||
SMALL: function(w: number, _h: number){
|
||||
return w <= 768;
|
||||
},
|
||||
/*
|
||||
Medium devices (landscape tablets, 768px and up)
|
||||
*/
|
||||
MEDIUM: function(w: number, _h: number){
|
||||
return w > 768;
|
||||
},
|
||||
/**
|
||||
* Large devices (laptops/desktops, 992px and up)
|
||||
*/
|
||||
LARGE: function(w: number, _h: number){
|
||||
return w > 992;
|
||||
},
|
||||
/**
|
||||
* Extra large devices (large laptops and desktops, 1200px and up)
|
||||
*/
|
||||
HUGE: function(w: number, _h: number){
|
||||
return w > 1200;
|
||||
},
|
||||
/**
|
||||
* Portrait mode
|
||||
*/
|
||||
PORTRAIT: function(w: number, h: number){
|
||||
return h > w;
|
||||
},
|
||||
/**
|
||||
* Landscape mode
|
||||
*/
|
||||
LANDSCAPE: function(w: number, h: number){
|
||||
return h < w;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom map to handle responsive events, a responsive event
|
||||
* consists of a validator and a callback
|
||||
*
|
||||
* When avalidator return true, its corresponding callback will
|
||||
* be called
|
||||
*/
|
||||
class ResponsiveHandle extends Map<TagResponsiveValidator,TagResponsiveCallbackRecord> {
|
||||
/**
|
||||
* Register a responsive envent to this map
|
||||
*
|
||||
* @param {TagResponsiveValidator} a validator
|
||||
* @param {TagResponsiveCallback} event callback
|
||||
* @memberof ResponsiveHandle
|
||||
*/
|
||||
on(validator: TagResponsiveValidator, callback: TagResponsiveCallback)
|
||||
{
|
||||
let record: TagResponsiveCallbackRecord = {
|
||||
callback: callback,
|
||||
fulfilled: false,
|
||||
}
|
||||
this.set(validator, record);
|
||||
}
|
||||
|
||||
/**
|
||||
* unregister a responsive event
|
||||
*
|
||||
* @param {TagResponsiveValidator} a validator
|
||||
* @memberof ResponsiveHandle
|
||||
*/
|
||||
off(validator: TagResponsiveValidator)
|
||||
{
|
||||
this.delete(validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify validators and execute callbacks
|
||||
*
|
||||
* @param {number} root tag width
|
||||
* @param {number} root tag height
|
||||
* @memberof ResponsiveHandle
|
||||
*/
|
||||
morph(rootw: number, rooth: number)
|
||||
{
|
||||
for (const [validator, record] of this.entries()) {
|
||||
const val = validator(rootw, rooth);
|
||||
if(record.fulfilled && val)
|
||||
{
|
||||
// the record has been previously fulfilled
|
||||
// and the validator returns true
|
||||
// nothing changed
|
||||
continue;
|
||||
}
|
||||
if(!record.fulfilled && !val)
|
||||
{
|
||||
// the record has been previously unfulfilled
|
||||
// and the validator returns false
|
||||
// nothing changed
|
||||
continue;
|
||||
}
|
||||
record.fulfilled =val;
|
||||
record.callback(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base abstract class for tag implementation, any AFX tag should be
|
||||
* subclass of this class
|
||||
@ -313,7 +475,17 @@ namespace OS {
|
||||
protected _mounted: boolean;
|
||||
|
||||
/**
|
||||
*Creates an instance of AFXTag.
|
||||
* a {@link ResponsiveHandle} to handle all responsive event
|
||||
* related to this tag
|
||||
*
|
||||
* @private
|
||||
* @memberof AFXTag
|
||||
*/
|
||||
private _responsive_handle: ResponsiveHandle;
|
||||
|
||||
private _responsive_check: (evt: TagEventType<{w: number, h: number}>) => void;
|
||||
/**
|
||||
* Creates an instance of AFXTag.
|
||||
* @memberof AFXTag
|
||||
*/
|
||||
constructor() {
|
||||
@ -324,6 +496,8 @@ namespace OS {
|
||||
}
|
||||
this._mounted = false;
|
||||
this.refs = {};
|
||||
this._responsive_handle = new ResponsiveHandle();
|
||||
this._responsive_check = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -394,6 +568,60 @@ namespace OS {
|
||||
return $(this).attr("data-id");
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter: Enable/disable responsive tag
|
||||
*
|
||||
* Getter: get responsive status
|
||||
*/
|
||||
set responsive(v:boolean) {
|
||||
if(!v)
|
||||
{
|
||||
this.attsw(false,"responsive");
|
||||
this.observable.off("resize",this._responsive_check);
|
||||
this._responsive_check = undefined;
|
||||
return;
|
||||
}
|
||||
if(this._responsive_check)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._responsive_check = (evt: TagEventType<{w: number, h: number}>) => {
|
||||
if(!evt || !evt.root || !evt.data.w || !evt.data.h )
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._responsive_handle.morph(evt.data.w, evt.data.h);
|
||||
}
|
||||
this.observable.on("resize", this._responsive_check);
|
||||
this.attsw(true, "responsive");
|
||||
}
|
||||
get responsive(): boolean {
|
||||
return this.hasattr("responsive");
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a responsive event to this tag
|
||||
*
|
||||
* @param {TagResponsiveValidator} responsive validator
|
||||
* @param {TagResponsiveCallback} responsive callback
|
||||
* @memberof AFXTag
|
||||
*/
|
||||
morphon(validator: TagResponsiveValidator, callback:TagResponsiveCallback)
|
||||
{
|
||||
this._responsive_handle.on(validator, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a responsive event from this tag
|
||||
*
|
||||
* @param {TagResponsiveValidator} responsive validator
|
||||
* @memberof AFXTag
|
||||
*/
|
||||
morphoff(validator: TagResponsiveValidator)
|
||||
{
|
||||
this._responsive_handle.off(validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a data to this tag
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user