This commit is contained in:
yumoqing 2024-02-26 18:22:18 +08:00
parent 96821a2b01
commit 81a82f46df
25 changed files with 2180 additions and 40 deletions

0
bricks/bricksNS.js Normal file
View File

1
bricks/bricksend.js Normal file
View File

@ -0,0 +1 @@
}

View File

@ -4,7 +4,7 @@ SOURCES=" factory.js uitypesdef.js utils.js i18n.js widget.js \
markdown_viewer.js video.js audio.js toolbar.js tab.js \
input.js registerfunction.js button.js accordion.js \
tree.js multiple_state_image.js form.js message.js \
paging.js datagrid.js dataviewer.js \
paging.js datagrid.js dataviewer.js iframe.js \
miniform.js wterm.js dynamicaccordion.js "
echo ${SOURCES}
cat ${SOURCES} > ../dist/bricks.js

View File

View File

@ -68,7 +68,6 @@ body {
.vcontainer {
display: flex;
flex-direction: column;
// overflow:auto;
}
.vbox {
@ -79,7 +78,6 @@ body {
.hcontainer {
display: flex;
flex-direction: row;
// overflow:auto;
}
.hbox {

View File

@ -0,0 +1,51 @@
var bricks = window.bricks || {};
bricks.DataGridAccordion = class extends bricks.VBox {
/*
options:{
data_url:
title:
description:
footer:
header:
record:
detail_url:
}
*/
constructor(opts):
super(opts);
if (self.opts.title){
var t = new bricks.Title3(
otext=self.opts.title,
i18n=true
);
this.add_widget(t);
}
if (self.opts.description){
var d = new bricks.Text(
otext=self.opts.description,
i18n=true,
wrap=true,
halign='left'
);
this.add_widget(d);
}
if (this.opts.header){
var h = await bricks.widgetBuild(this.opts.header, this);
if (h){
this.add_widgettype(h);
}
}
var filler = new bricks.Filler();
var this.accordion = await this.buildAccordion();
filler.add_widget(this.accordion)
this.add_widget(filler);
if (this.opts.footer){
var f = await bricks.widgetBuild(this.opts.footer, this);
this.add_widget(f);
}
}
async buildAccordion(){
var p = bricks.VScrollPanel();
}
}

239
bricks/dynamicaccordion.js Normal file
View File

@ -0,0 +1,239 @@
var bricks = window.bricks || {};
bricks.PageContent = class extends bricks.Layout {
constructor(opts){
super(opts);
}
}
bricks.AccordionItem = class extends bricks.Layout {
constructor(opts){
super(opts);
this.set_css('accordion-item');
}
}
bricks.AccordionHeader = class extends bricks.Layout {
constructor(opts){
super(opts);
this.set_css('accordion-item-header');
}
}
bricks.DynamicAccordion = class extends bricks.VScrollPanel {
/*
{
"title":
"description",
"data_url",
"content_url"
"content_view"
"cache_limit",
"page_rows",
"header":
"record_view"
"record_show_field":
}
*/
constructor(opts){
super(opts);
this.set_css("accordion");
this.element = this.dom_element;
this.dataUrl = this.opts.data_url;
this.base_params = opts.params || {};
this.rows = parseInt(this.opts.page_rows);
this.lastPage = null;
this.contentUrl = this.opts.content_url;
this.pages = new Map(); // 用于存储已加载的页面数据
this.pageContents = new Map(); // 用于存储已加载的页面内容
this.active_content = null;
this.loading = false;
this.cacheLimit = this.opts.cache_limit;
if (this.opts.content_view){
this.content_view_tmpl = JSON.stringify(this.opts.content_view);
}
if (this.opts.record_view){
this.record_view_tmpl = JSON.stringify(this.opts.record_view);
}
this.bind('min_threshold', this.load_previous_page.bind(this));
this.bind('max_threshold', this.load_next_page.bind(this));
this.render();
}
render(params) {
this.clear_widgets();
this.pages = new Map(); // 用于存储已加载的页面数据
this.pageContents = new Map(); // 用于存储已加载的页面内容
params = params || {};
this.params = bricks.extend(this.base_params, params);
this.loadPage(1);
}
async loadPage(page) {
if (this.loading){
return;
}
this.loading = true;
// 检查页面是否在缓存中
if (! this.pages.has(page)) {
var jc = new bricks.HttpJson();
var params = bricks.extend({}, this.params);
params = bricks.extend(params,{
page:page,
rows:this.rows
});
var d = await jc.get(this.dataUrl,{params:params});
if (!d){
console.log(this.dataUrl,{params:params}, 'error');
this.loading = false;
return;
}
var data = d.rows;
this.lastPage = Math.ceil(d.total / this.rows);
this.pages.set(page, data); // 将数据缓存起来
await this.renderAccordionItems(data, page);
// 检查缓存是否已满
if (this.pages.size > this.cacheLimit) {
// 删除当前页最远的一页
var max, min;
var pages = Array.from(this.pages.keys());
max = Math.max(...pages);
min = Math.min(...pages);
const farthestPage = page == max? min : max;
var w = this.pageContents.get(farthestPage);
this.remove_widget(w);
this.pages.delete(farthestPage);
this.pageContents.delete(farthestPage);
}
var pages = Array.from(this.pages.keys());
if (page == Math.min(pages)){
this.scrollY = 1 / pages.length;
if (this.scrollY == 1){
this.scrollY = 0;
}
} else {
this.scrollY = (pages.length - 1) / pages.length;
}
}
this.loading = false;
return;
}
async renderAccordionItems(data, page) {
const pageContent = new bricks.PageContent({height:"auto"});
pageContent.page = page;
for (var i=0;i<data.length;i++){
const record = data[i];
const item = new bricks.AccordionItem({});
const header = new bricks.AccordionHeader({});
if (this.opts.record_view){
var w;
w = await bricks.widgetBuildWithData(this.record_view_tmpl,
header, record);
if (w){
header.add_widget(w);
} else {
console.log('bricks.widgetBuildWithData(), tmpl=', this.record_view_tmpl, 'record=', record);
}
}
header.textContent = record.title; // 使用数据记录的标题字段
var content = new bricks.VBox({
height:'auto',
display:'none'
});
content.set_css('accordion-item-content');
content.hide();
header.bind('click', this.toggle_content.bind(this, content, record));
item.add_widget(header);
item.add_widget(content);
pageContent.add_widget(item);
};
this.pageContents.set(page, pageContent);
var pages = Array.from(this.pages.keys());
if (page == Math.min(...pages)){
this.add_widget(pageContent, 0);
} else {
this.add_widget(pageContent);
}
}
async toggle_content(content, record, event){
if (this.active_content){
if (this.active_content != content){
this.active_content.clear_widgets();
this.active_content.hide();
this.active_content = null;
}
}
content.toggle_hide();
if (content.dom_element.style.display == 'none'){
console.log('content just collapsed');
content.clear_widgets();
this.active_content = null;
return;
}
var jc = new bricks.HttpJson();
var w;
if (this.content_view_tmpl){
w = await bricks.widgetBuildWithData(this.content_view_tmpl,
content, record);
} else if (this.contentUrl){
var url = this.contentUrl;
var params = {
'id':record.id
}
var desc = await jc.get(url, {params:params});
w = await bricks.widgetBuild(desc, content);
}
if (w){
content.add_widget(w);
this.active_content = content;
} else {
this.active_content = null;
content.hide();
}
}
get_load_page(dir){
var pages = Array.from(this.pages.keys());
if (dir=='up'){
var page = Math.min(...pages);
console.log('page=', page, pages, 'dir=', dir);
return page - 1;
} else {
var page = Math.max(...pages);
console.log('page=', page, pages, 'dir=', dir);
return page + 1;
}
}
async load_previous_page(){
var pages = Array.from(this.pages.keys());
var page = this.get_load_page('up');
if (page > 0){
await this.loadPage(page);
console.log('page loaded, page=', page);
} else {
var pages = Array.from(this.pages.keys());
console.log('page NOT loaded, page=', page,
'lastPage=', this.lastPage,
'rows=', this.rows,
'cacheLimit=', this.cacheLimit,
'buffered page=', pages.length);
}
}
async load_next_page(){
var pages = Array.from(this.pages.keys());
var page = this.get_load_page('down');
if (page <= this.lastPage){
await this.loadPage(page);
console.log('page Loaded, page=', page);
} else {
console.log('page=', page,
'lastPage=', this.lastPage,
'rows=', this.rows,
'cacheLimit=', this.cacheLimit,
'buffered page=', pages);
}
}
}
bricks.Factory.register('DynamicAccordion', bricks.DynamicAccordion);

View File

@ -21,9 +21,9 @@ bricks.FormBody = class extends bricks.VBox {
opts.width = '100%';
opts.scrollY = 'scroll';
super(opts);
console.log('dxcygvuhbijnokmpl,;lmknjbhvgcfx');
this.name_inputs = {};
this.form_body = new bricks.Layout({width:'100%',
height:"100%",
overflow:'auto'
});
this.add_widget(this.form_body);
@ -129,13 +129,6 @@ bricks.Form = class extends bricks.VBox {
*/
constructor(opts){
super(opts);
if (this.opts.title){
var t = new bricks.Title3({
otext:this.opts.title,
height:'auto',
i18n:true});
this.add_widget(t);
}
if (this.opts.description){
var d = new bricks.Text({
otext:this.opts.description,
@ -147,6 +140,13 @@ bricks.Form = class extends bricks.VBox {
this.add_widget(this.body);
if (! opts.notoolbar)
this.build_toolbar(this);
if (this.opts.title){
var t = new bricks.Title3({
otext:this.opts.title,
height:'auto',
i18n:true});
this.add_widget(t, 0);
}
}
build_toolbar(widget){
var box = new bricks.HBox({height:'auto', width:'100%'});

View File

@ -5,12 +5,18 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css" />
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet" />
<link rel="stylesheet" href="{{entire_url('/bricks/css/bricks.css')}}">
<!-- support IE8 (for Video.js versions prior to v7) -->
<script src="https://vjs.zencdn.net/ie8/1.1.2/videojs-ie8.min.js"></script>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/xterm@4.19.0/lib/xterm.js"></script>
<!--- <script src="https://vjs.zencdn.net/7.14.3/video.js"></script> --->
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
<script src="{{entire_url('/3parties/recorder.wav.min.js')}}"></script>
<!--- <script src="{{entire_url('/3parties/videojs-contrib-hls.min.js')}}"></script> --->
<script src="{{entire_url('/bricks/bricks.js')}}"></script>
<script>
const opts = {

12
bricks/iframe.js Normal file
View File

@ -0,0 +1,12 @@
var bricks = window.bricks || {};
bricks.Iframe = class extends bricks.Layout {
constructor(opts){
super(opts);
this.dom_element.src = opts.url;
}
create(){
this.dom_element = document.createElement('iframe');
}
}
bricks.Factory.register('Iframe', bricks.Iframe);

View File

@ -36,20 +36,23 @@ bricks.Image = class oops extends bricks.JsWidget {
bricks.Icon = class oops extends bricks.Image {
constructor(opts){
var rate = opts.rate || 1;
var siz = bricks.app.charsize * rate + 'px';
opts.width = siz;
opts.height = siz;
super(opts);
this.opts.width = bricks.app.charsize;
this.opts.height = bricks.app.charsize;
this.rate = rate;
this.ctype = 'text';
this.sizable();
this.set_fontsize();
}
change_fontsize(ts){
var siz = bricks.app.charsize;
var siz = bricks.app.charsize * this.rate + 'px';
this.set_width(siz);
this.set_height(siz);
}
set_fontsize(){
var siz = bricks.app.charsize;
var siz = bricks.app.charsize * this.rate + 'px';
this.set_width(siz);
this.set_height(siz);
}
@ -63,20 +66,19 @@ bricks.Icon = class oops extends bricks.Image {
bricks.BlankIcon = class oops extends bricks.JsWidget {
constructor(opts){
opts.width = bricks.app.charsize;
opts.height = bricks.app.charsize;
super(opts);
this.rate = opts.rate || 1;
this.ctype = 'text';
this.sizable();
this.set_fontsize();
}
change_fontsize(ts){
var siz = bricks.app.charsize + 'px';
var siz = bricks.app.charsize * this.rate + 'px';
this.set_width(siz);
this.set_height(siz);
}
set_fontsize(){
var siz = bricks.app.charsize + 'px';
var siz = bricks.app.charsize * this.rate + 'px';
this.set_width(siz);
this.set_height(siz);
}

View File

@ -8,11 +8,13 @@ bricks.Layout = class extends bricks.JsWidget {
add_widget(w, index){
if (! index || index>=this.children.length){
// append child at end
w.parent = this;
this.children.push(w);
this.dom_element.appendChild(w.dom_element);
return
}
// insert to where index point out
var pos_w = this.children[index];
this.dom_element.insertBefore(w.dom_element, pos_w.dom_element);
this.children.insert(index+1, w);
@ -105,6 +107,6 @@ bricks.HFiller = class extends bricks.Layout {
bricks.Factory.register('HBox', bricks.HBox);
bricks.Factory.register('VBox', bricks.VBox);
bricks.Factory.register('Filler', bricks.Filler);
bricks.Factory.register('HFiller', bricks.HFiller);
bricks.Factory.register('VFiller', bricks.VFiller);
bricks.Factory.register('HFiller', bricks.Filler);
bricks.Factory.register('VFiller', bricks.Filler);

View File

@ -26,8 +26,8 @@ bricks.HScrollPanel = class extends bricks.HBox {
constructor(opts){
opts.width = '100%';
opts.height = '100%';
opts.overflow = 'auto';
super(opts);
this.set_css('hscroll');
this.min_threshold = opts.min_threshold || 0.02;
this.max_threshold = opts.max_threshold || 0.95;
this.bind('scroll', this.scroll_handle.bind(this))
@ -63,8 +63,8 @@ bricks.VScrollPanel = class extends bricks.VBox {
constructor(opts){
opts.width = '100%';
opts.height = '100%';
opts.overflow = 'auto';
super(opts);
this.set_css('vscroll');
this.min_threshold = opts.min_threshold || 0.02;
this.max_threshold = opts.max_threshold || 0.95;
this.bind('scroll', this.scroll_handle.bind(this))

View File

@ -3,7 +3,7 @@ var bricks = window.bricks || {};
we use videojs for video play
https://videojs.com
*/
bricks.Video = class extends bricks.JsWidget {
bricks.Video = class extends bricks.Layout {
/*
{
vtype:
@ -18,24 +18,44 @@ bricks.Video = class extends bricks.JsWidget {
this.set_css('vjs-big-play-centered');
this.set_css('vjs-fluid');
this.cur_url = options.url;
this.cur_vtype = options.vtype;
console.log('here1');
this.cur_vtype = options.type;
schedule_once(this.create_player.bind(this), 0.1);
this.hidedbtn = new bricks.Button({text:'click me'});
this.hidedbtn.hide();
this.add_widget(this.hidedbtn);
this.hidedbtn.bind('click', this.play.bind(this));
}
create(){
this.dom_element = document.createElement('video');
var e;
e = document.createElement('video');
e.controls = true;
this.dom_element = e;
}
auto_play(){
this.hidedbtn.dispatch('click');
}
play(){
this.player.play();
}
create_player(){
console.log('here2');
this.player = videojs(this.dom_element);
console.log('here3');
this._set_source();
if (this.opts.autoplay){
// schedule_once(this.auto_play.bind(this), 0.1);
;
}
console.log('here3', this.player);
}
_set_source(){
console.log('--------set source ---------');
}
set_source(url){
this.player.src({
src:this.cur_url,
type:this.cur_vtype
});
}
set_source(url, vtype){
this.cur_url = url;
this.cur_vtype = vtype;
this._set_source();
}
}

BIN
examples/.channels.ui.swp Normal file

Binary file not shown.

10
examples/channels.dspy Normal file
View File

@ -0,0 +1,10 @@
sql = """select id, tv_name as title, url, logo_url from iptvchannels"""
ns = {k:v for k,v in params_kw.items() }
ns['sort'] = 'title'
print(f'{ns=}')
db = DBPools()
async with db.sqlorContext('iptvdb') as sor:
d = await sor.sqlPaging(sql, ns)
print(f'get {len(d["rows"])} records')
return d

64
examples/channels.ui Normal file
View File

@ -0,0 +1,64 @@
{
"widgettype":"DynamicAccordion",
"options":{
"data_url":"{{entire_url('./channels.dspy')}}",
"content_url":"{{entire_url('play_channel.dspy')}}",
"content_view":{
"widgettype":"VBox",
"options":{
"overflow":"auto",
"width":"90%",
"height":"500px"
},
"subwidgets":[
{
"id":"toolbar",
"widgettype":"Toolbar",
"options":{
"orientation":"horizontal",
"tool_margin":"15px",
"tools":[
{
"name":"blocked",
"label":"Blocked it"
}
]
}
},
{
"id":"videoplayer",
"widgettype":"Video",
"options":{
"type":"application/x-mpegURL",
"url":"${url}"
}
}
]
},
"record_view":{
"widgettype":"HBox",
"options":{
"height":"100%",
"width":"100%"
},
"subwidgets":[
{
"widgettype":"Icon",
"options":{
"rate":2,
"url":"${logo_url}"
}
},
{
"widgettype":"Text",
"options":{
"text":"${title}"
}
}
]
},
"page_rows":800,
"cache_limit":5
}
}

View File

@ -0,0 +1,40 @@
{
"widgettype":"DynamicAccordion",
"options":{
"data_url":"{{entire_url('./channels.dspy')}}",
"content_url":"{{entire_url('play_channel.dspy')}}",
"content_view":{
"id":"videoplayer",
"widgettype":"Iframe",
"options":{
"width":"100%",
"url":"${url}"
}
},
"record_view":{
"widgettype":"HBox",
"options":{
"height":"100%",
"width":"100%"
},
"subwidgets":[
{
"widgettype":"Icon",
"options":{
"rate":2,
"url":"${logo_url}"
}
},
{
"widgettype":"Text",
"options":{
"text":"${title}"
}
}
]
},
"page_rows":800,
"cache_limit":5
}
}

6
examples/footer.ui Normal file
View File

@ -0,0 +1,6 @@
{
"widgettype":"Text",
"options":{
"text":"Bricks module, make front end coding easier"
}
}

8
examples/iframe.ui Normal file
View File

@ -0,0 +1,8 @@
{
"id":"videoplayer",
"widgettype":"Iframe",
"options":{
"width":"100%",
"url":"{{params_kw.url}}"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,27 @@
{
"widgettype":"urlwidget",
"widgettype":"VBox",
"options":{
"url":"scroll.json"
}
"width":"100%",
"height":"100%"
},
"subwidgets":[
{
"widgettype":"Title3",
"options":{
"text":"Scroll Widget"
}
},
{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('scroll.json')}}"
}
},
{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('footer.ui')}}"
}
}
]
}

33
examples/t.ui Normal file
View File

@ -0,0 +1,33 @@
{
"widgettype":"VBox",
"options":{
"width":"100%"
},
"subwidgets":[
{
"widgettype":"Title3",
"options":{
"height":"auto",
"text":"Header information"
}
},
{
"widgettype":"Filler",
"options":{},
"subwidgets":[
{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('channels.ui')}}"
}
}
]
},
{
"widgettype":"Text",
"options":{
"text":"Footer information"
}
}
]
}

35
examples/tree_t.ui Normal file
View File

@ -0,0 +1,35 @@
{
"widgettype":"VBox",
"options":{
"height":"100%",
"width":"100%"
},
"subwidgets":[
{
"widgettype":"Title3",
"options":{
"height":"auto",
"text":"This is a tree"
}
},
{
"widgettype":"Filler",
"options":{},
"subwidgets":[
{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('tree.ui')}}"
}
}
]
},
{
"widgettype":"Text",
"options":{
"height":"auto",
"text":"This is footer message"
}
}
]
}

View File

@ -1,10 +1,10 @@
{
"id":"videoplayer",
"widgettype":"VideoPlayer",
"widgettype":"Video",
"options":{
"autoplay":true,
"url":"http://kimird.com/video/sample-mp4-file.mp4",
"type":"mp4"
"type":"video/mp4"
}
}