cedro 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -16,13 +16,13 @@
16
16
  "ui-library",
17
17
  "widget-based"
18
18
  ],
19
- "version": "0.1.32",
19
+ "version": "0.1.34",
20
20
  "type": "module",
21
21
  "devDependencies": {
22
22
  "@fontsource/roboto": "^5.0.5",
23
23
  "@oothkoo/seo-js": "1.0.5",
24
24
  "@types/color": "3.0.3",
25
- "@types/node": "^20.4.4",
25
+ "@types/node": "^20.19.33",
26
26
  "@types/react": "18.2.17",
27
27
  "color": "4.2.3",
28
28
  "glob": "^11.0.0",
@@ -1,3 +1,4 @@
1
+ import { RouteItem } from "src/interfaces/application.interface";
1
2
  import { Widget, WidgetAlignTypes } from "../ui";
2
3
  import { createWidget } from "../ui/widget.builder";
3
4
  import Application, { ApplicationProps } from "./application.core";
@@ -65,6 +66,21 @@ export function createApplication(content: any): Application {
65
66
  newApp.setRouterHostId(item.getAttribute("w-host-id"));
66
67
  item.childNodes.forEach((ietmRoute: any) => {
67
68
  if (ietmRoute.getAttribute("w-route-path") && ietmRoute.getAttribute("href")) {
69
+ let newRoute: RouteItem = {
70
+ src: "",
71
+ label: undefined,
72
+ icon: undefined
73
+ };
74
+ if (ietmRoute.getAttribute("href")) {
75
+ newRoute.src = decode(ietmRoute.getAttribute("href"));
76
+ }
77
+ if (ietmRoute.getAttribute("w-label")) {
78
+ newRoute.label = ietmRoute.getAttribute("w-label");
79
+ }
80
+ if (ietmRoute.getAttribute("w-icon")) {
81
+ newRoute.icon = ietmRoute.getAttribute("w-icon");
82
+ }
83
+ newApp.addRoute(newRoute);
68
84
  newApp.router.on(decode(ietmRoute.getAttribute("href")), () => {
69
85
  const url = decode(ietmRoute.getAttribute("href"));
70
86
  const isProduction = process.env.NODE_ENV === "production";
@@ -76,6 +92,7 @@ export function createApplication(content: any): Application {
76
92
  if (host) {
77
93
  newApp.attachWidget(newApp.getLoadedModule() as Widget, host);
78
94
  }
95
+ newApp.run("location-change");
79
96
  newApp.hideLoading();
80
97
  });
81
98
 
@@ -96,10 +113,11 @@ export function createApplication(content: any): Application {
96
113
  };
97
114
 
98
115
  if (isProduction) loadCss();
116
+
99
117
  });
100
118
  }
101
119
  });
102
- newApp.router.on("/", () => {});
120
+ newApp.router.on("/", () => { });
103
121
  newApp.router.resolve();
104
122
  }
105
123
  });
@@ -8,7 +8,7 @@ import "@fontsource/roboto/900.css";
8
8
  import "material-icons/iconfont/material-icons.css";
9
9
  import { Widget } from "../ui/widget.ui";
10
10
  import { Screen } from "./screeen.core";
11
- import { IApplication, IScreenSize } from "../interfaces/application.interface";
11
+ import { IApplication, IScreenSize, RouteItem } from "../interfaces/application.interface";
12
12
  import { IWidget, WUICallback, WUIEvent } from "../interfaces/widget.interface";
13
13
  import Navigo from "navigo";
14
14
  import { Dialog } from "../ui/dialog";
@@ -42,6 +42,7 @@ class WApplication implements IApplication {
42
42
 
43
43
  screen: Screen;
44
44
  root: Widget;
45
+ routes: Array<RouteItem>;
45
46
  router: Navigo;
46
47
  routerHostId: string;
47
48
 
@@ -64,6 +65,7 @@ class WApplication implements IApplication {
64
65
 
65
66
  this.root.setType(WidgetTypes.FILL);
66
67
  this.screen = new Screen();
68
+ this.routes = new Array<RouteItem>();
67
69
  this.router = new Navigo("/");
68
70
  this.routerHostId = this.getRoot().id;
69
71
 
@@ -146,8 +148,8 @@ class WApplication implements IApplication {
146
148
  public alert(
147
149
  title: string = "",
148
150
  msg: string,
149
- onOk: () => void = () => {},
150
- onCancell: () => void = () => {}
151
+ onOk: () => void = () => { },
152
+ onCancell: () => void = () => { }
151
153
  ): void {
152
154
  const mesageLabel = new Label("alert.label", "span");
153
155
 
@@ -170,8 +172,8 @@ class WApplication implements IApplication {
170
172
 
171
173
  public confirm(
172
174
  msg: string,
173
- onOk: () => void = () => {},
174
- onCancell: () => void = () => {}
175
+ onOk: () => void = () => { },
176
+ onCancell: () => void = () => { }
175
177
  ): void {
176
178
  const mesageLabel = new Label("alert.label", "span");
177
179
 
@@ -227,6 +229,11 @@ class WApplication implements IApplication {
227
229
  this.mediaQueries.set(query, { minWidth, maxWidth, cb });
228
230
  }
229
231
 
232
+ public addRoute(route: RouteItem): void {
233
+ //add route.
234
+ this.routes.push(route);
235
+ }
236
+
230
237
  /**
231
238
  * Initializes the application.
232
239
  *
@@ -303,6 +310,7 @@ export type ApplicationProps = {
303
310
  children: any;
304
311
  onResize?: (args: any) => void;
305
312
  onLoad?: (args: any) => void;
313
+ onLocationChange?: (args: any) => void;
306
314
  };
307
315
 
308
316
  export const Application = (props: ApplicationProps) => {
@@ -314,6 +322,10 @@ export const Application = (props: ApplicationProps) => {
314
322
  connectApplication({ event: "resize", then: props.onResize });
315
323
  }
316
324
 
325
+ if (props.onLocationChange) {
326
+ connectApplication({ event: "location-change", then: props.onLocationChange });
327
+ }
328
+
317
329
  return (
318
330
  <div
319
331
  title={props.title}
@@ -349,10 +361,12 @@ export const Routes = (props: RoutesProps) => {
349
361
 
350
362
  export type RouteProps = {
351
363
  src: string;
364
+ label?: string;
365
+ icon?: string;
352
366
  };
353
367
 
354
368
  export const Route = (props: RouteProps) => {
355
- return <a w-route-path href={props.src}></a>;
369
+ return <a w-route-path href={props.src} w-label={props.label} w-icon={props.icon}></a>;
356
370
  };
357
371
 
358
372
  export default WApplication;
@@ -1,7 +1,8 @@
1
1
  export const getAdaptedUrl = (url: string) => {
2
2
  const timestamp = new Date().getTime();
3
3
  const isProduction = process.env.NODE_ENV === "production";
4
- const pathDev = `../../../../src${url}index.js?ts=${timestamp}`;
4
+ //const pathDev = `../../../../src${url}index.js?ts=${timestamp}`;
5
+ const pathDev = ".." + url + "?ts=" + timestamp;
5
6
  const pathProduction = `../../assets${url}/index.js?ts=${timestamp}`;
6
7
  return isProduction ? pathProduction : pathDev;
7
8
  };
@@ -16,10 +16,17 @@ export enum WUIThemes {
16
16
  DARK = "dark",
17
17
  }
18
18
 
19
+ export interface RouteItem {
20
+ src: string;
21
+ label?: string;
22
+ icon?: string;
23
+ }
24
+
19
25
  export interface IApplication {
20
26
  seo: Seo;
21
27
  screen: IScreen;
22
28
  root: IWidget;
29
+ routes: Array<RouteItem>;
23
30
  router: Navigo;
24
31
  alertDialog: Dialog;
25
32
  confirmDialog: Dialog;
@@ -44,6 +51,8 @@ export interface IApplication {
44
51
  cb: (app: IApplication) => void
45
52
  ): void;
46
53
 
54
+ addRoute(route: RouteItem): void;
55
+
47
56
  init(): void;
48
57
  setRoot(root: IWidget): void;
49
58
  getRoot(): IWidget;
@@ -4,6 +4,7 @@ import { Vector2D } from "../types/vector2d.type";
4
4
  export type WUIEvent =
5
5
  | "widget-load"
6
6
  | "load"
7
+ | "location-change"
7
8
  | "click"
8
9
  | "resize"
9
10
  | "mousedown"
@@ -7,6 +7,8 @@ import { UID } from "../core/uid";
7
7
  import { WidgetAlignTypes, WidgetTypes } from "./widget.types";
8
8
  import { normalizeWidget } from "./widget.normalize";
9
9
 
10
+ export type IconButtonIconPosition = "left" | "right";
11
+
10
12
  export class IconButton extends Button {
11
13
  icon: Icon;
12
14
  label: Label;
@@ -15,10 +17,13 @@ export class IconButton extends Button {
15
17
  showText: boolean;
16
18
  centerX: boolean;
17
19
 
20
+ iconPosition: IconButtonIconPosition = "left";
21
+
18
22
  constructor(id: string, icon: string = "dark_mode", parent: Widget | null = null) {
19
23
  super(id, parent);
20
24
 
21
25
  this.centerX = false;
26
+ this.iconPosition = "left";
22
27
 
23
28
  this.setAlign(WidgetAlignTypes.HORIZONTAL);
24
29
  this.icon = new Icon(id + ".icon", icon, undefined, this);
@@ -96,21 +101,48 @@ export class IconButton extends Button {
96
101
  const requiredWidth = labelWidth + iconWidth + padding * 5;
97
102
 
98
103
  const labelHeight = this.label.getBody().clientHeight;
104
+ let startX = 0;
105
+ let startY = 0;
106
+ let startLabelX = 0;
107
+ let startLabelY = 0;
108
+ let spaceBetLabelAndIcon = padding * 2;
99
109
 
100
- let startX = availableWidth / 2 - (iconWidth + padding + labelWidth) / 2;
110
+ if (this.iconPosition == "left") {
101
111
 
102
- if (availableWidth < requiredWidth) {
103
- startX = padding * 2;
104
- }
112
+ startX = availableWidth / 2 - (iconWidth + spaceBetLabelAndIcon + labelWidth) / 2;
105
113
 
106
- if (!this.centerX) {
107
- startX = padding * 2;
108
- }
114
+ if (availableWidth < requiredWidth) {
115
+ startX = padding * 2;
116
+ }
117
+
118
+ if (!this.centerX) {
119
+ startX = padding * 2;
120
+ }
121
+
122
+ startLabelX = startX + iconWidth + spaceBetLabelAndIcon;
123
+
124
+ startY = this.getH() / 2 - iconWidth / 2;
125
+ startLabelY = this.getH() / 2 - labelHeight / 2;
126
+
127
+
128
+ } else if (this.iconPosition == "right") {
109
129
 
110
- const startLabelX = startX + iconWidth + padding;
130
+ startLabelX = availableWidth / 2 - (iconWidth / 2 + spaceBetLabelAndIcon + labelWidth) / 2;
111
131
 
112
- let startY = this.getH() / 2 - iconWidth / 2;
113
- let startLabelY = this.getH() / 2 - labelHeight / 2;
132
+ if (!this.centerX) {
133
+ startLabelX = padding * 2;
134
+ }
135
+
136
+ startX = startLabelX + labelWidth + spaceBetLabelAndIcon;
137
+
138
+ if (availableWidth < requiredWidth) {
139
+ startLabelX = padding * 2;
140
+ }
141
+
142
+ startY = this.getH() / 2 - iconWidth / 2;
143
+ startLabelY = this.getH() / 2 - labelHeight / 2;
144
+
145
+ }
114
146
 
115
147
  if (this.getType() !== WidgetTypes.FILL) {
116
148
  startY = this.getH() / 2 - this.icon.getH() / 2;
@@ -126,7 +158,7 @@ export class IconButton extends Button {
126
158
  }
127
159
 
128
160
  this.icon.setX(startX);
129
- this.label.setX(startLabelX + padding);
161
+ this.label.setX(startLabelX);
130
162
 
131
163
  this.icon.setY(startY);
132
164
  this.label.setY(startLabelY);
@@ -147,6 +179,10 @@ export class IconButton extends Button {
147
179
  public setCenterX(centerX: boolean): void {
148
180
  this.centerX = centerX;
149
181
  }
182
+
183
+ public setIconPosition(position: IconButtonIconPosition = "left") {
184
+ this.iconPosition = position;
185
+ }
150
186
  }
151
187
 
152
188
  export type wIconButtonProps = Omit<wButtonProps, "text"> & {
@@ -154,6 +190,7 @@ export type wIconButtonProps = Omit<wButtonProps, "text"> & {
154
190
  text?: string | null;
155
191
  onlyIcon?: boolean | null;
156
192
  centerX?: boolean | null;
193
+ iconPosition?: IconButtonIconPosition | null;
157
194
  };
158
195
 
159
196
  export const WIconButton = (props: wIconButtonProps) => {
@@ -175,6 +212,7 @@ export const WIconButton = (props: wIconButtonProps) => {
175
212
  w-width={props.width}
176
213
  w-height={props.height}
177
214
  w-center-x={props.centerX}
215
+ w-icon-position={props.iconPosition}
178
216
  />,
179
217
  props
180
218
  );
@@ -193,6 +231,7 @@ export function createIconButton(
193
231
  const dataHeight = content.getAttribute("w-height");
194
232
  const dataOnlyIcon = content.getAttribute("w-only-icon");
195
233
  const dataCenterX = content.getAttribute("w-center-x");
234
+ const dataIconPosition = content.getAttribute("w-icon-position");
196
235
 
197
236
  let newIconButton = new IconButton(id, dataIcon, parent);
198
237
 
@@ -224,5 +263,9 @@ export function createIconButton(
224
263
  newIconButton.setCenterX(true);
225
264
  }
226
265
 
266
+ if (dataIconPosition) {
267
+ newIconButton.setIconPosition(dataIconPosition);
268
+ }
269
+
227
270
  return newIconButton;
228
271
  }
@@ -0,0 +1,137 @@
1
+ /*
2
+ * Widget: Breadcums
3
+ * Descripcion:
4
+ * El widget muestra la ruta en la que esta parado el usuario.
5
+ * Por ejemplo:
6
+ * - Si el usuario esta en https://mipagina.com/admin/user/profile
7
+ * El widget debe mostrar algo asi como Admin >> Usuario >> Perfil.
8
+ * Implementacion:
9
+ * Una idea es que este widget este conectado con las rutas definidas en la aplicacion.
10
+ * Entonces a las rutas le podemos agregar la propiedad de label o breadcumbLabel que
11
+ * indicaria el texto a mostrar cuando se esta en esa ruta.
12
+ * Ademas tambien le podemos definir un icono a cada ruta.
13
+ *
14
+ * Luego el widget Breadcumbs puede buscar en las rutas de la aplicacion, cual es la ruta actual
15
+ * y actualizarse mostrando graficamente donde se encuentra el usuario.
16
+ * Para esto podriamos agregar algun evento en las rutas o en la aplicacion que se dispare cuando
17
+ * se carga una url y donde los widget puedan suscribirse para capturar ese evento y hacer algo
18
+ * en consecuencia.
19
+ *
20
+ * Vamos a usar el widget de toolbar configurado como barra de herramientas horizontal y los botenes
21
+ * que usa la barra de herramientas vamos a configurarlos para que muestren el icono hacia la derecha.
22
+ *
23
+ * */
24
+
25
+ import { Toolbar } from "./toolbar.ui";
26
+ import { connectWidgetCallback, getOnlyEventProps, Widget } from "./widget.ui";
27
+ import { WidgetProps } from "./widget.types";
28
+ import { normalizeWidget } from "./widget.normalize";
29
+ import { UID } from "../core/uid";
30
+ import { Button } from "./button.ui";
31
+ import { IconButton } from "./IconButton.ui";
32
+ import { RouteItem } from "src/interfaces/application.interface";
33
+
34
+ export class Breadcumbs extends Toolbar {
35
+ constructor(id: string, parent: Widget | null) {
36
+ super(id, parent, "horizontal");
37
+ this.setVariant("contained");
38
+ const connectEvent = () => {
39
+ if (!window.app) {
40
+ setTimeout(connectEvent, 500);
41
+ }
42
+ window.app?.subscribe({
43
+ event: "location-change", then: () => {
44
+ this.configItems();
45
+ }
46
+ })
47
+ this.configItems();
48
+ }
49
+ connectEvent();
50
+ }
51
+
52
+ private getRoute(name: string): RouteItem | null {
53
+ if (!window.app) return null;
54
+ const app = window.app;
55
+
56
+ for (const route of app.routes) {
57
+
58
+ const parts = route.src.split("/").filter((item) => item != "");
59
+ const last = parts[parts.length - 1];
60
+
61
+ if (!last) continue;
62
+
63
+ if (last == name) {
64
+ return route;
65
+ }
66
+
67
+ }
68
+
69
+ return null;
70
+ }
71
+
72
+ private configItems() {
73
+ if (!window.app) return;
74
+ const app = window.app;
75
+ const router = window.app.router;
76
+ if (!router) return;
77
+
78
+ for (const child of this.items.values()) {
79
+ this.deleteItem(child.id);
80
+ }
81
+
82
+ const url = router.getCurrentLocation().url;
83
+ const path = url.split("/");
84
+ for (let i = 0; i < path.length; i++) {
85
+ const itemName = path[i];
86
+ const route = this.getRoute(itemName);
87
+ if (!route) continue;
88
+ const newWidget = new Button("item." + itemName, this);
89
+ let widgetText = route.label ? route.label : itemName;
90
+ newWidget.setText(widgetText.replaceAll(" ", "&nbsp;"))
91
+ newWidget.setVariant("text");
92
+ newWidget.subscribe({
93
+ event: "click", then: () => {
94
+ app.goTo(route.src);
95
+ }
96
+ })
97
+ this.addItem(newWidget.id, newWidget);
98
+
99
+ if (i == path.length - 1) {
100
+ //El ultimo no lleva separador
101
+ break;
102
+ }
103
+ const separator = new IconButton("icon." + itemName, "keyboard_double_arrow_right", this);
104
+ separator.setVariant("text");
105
+ separator.setW(50);
106
+ this.addItem(separator.id, separator);
107
+ separator.setW(20);
108
+ }
109
+
110
+ this.render();
111
+ }
112
+ }
113
+
114
+ export type wBreadcumbProps = WidgetProps & {
115
+ };
116
+
117
+ export const WBreadcumbs = (props: wBreadcumbProps) => {
118
+ if (!props.id) {
119
+ props.id = "breadcumbs." + UID();
120
+ }
121
+
122
+ connectWidgetCallback(props.id, getOnlyEventProps(props));
123
+
124
+ return normalizeWidget(
125
+ <div
126
+ id={props.id}
127
+ w-breadcumbs
128
+ ></div>,
129
+ props
130
+ );
131
+ };
132
+
133
+ export function createBreadcumbs(id: string, _content: any, parent: Widget | null = null): Breadcumbs {
134
+ let newBreadcumbs = new Breadcumbs(id, parent);
135
+
136
+ return newBreadcumbs;
137
+ }
package/src/ui/index.ts CHANGED
@@ -27,6 +27,7 @@ import { VPanel, WVPanel } from "./vpanel.ui";
27
27
  import { createWidget } from "./widget.builder";
28
28
  import { IconButtonMenu, WIconButtonMenu, WIconButtonMenuItem } from "./iconButtonMenu.ui";
29
29
  import { WidgetAlignTypes, WidgetTypes } from "./widget.types";
30
+ import { Breadcumbs, WBreadcumbs } from "./breadcumbs.ui";
30
31
 
31
32
  export type { ContainerParams };
32
33
 
@@ -36,6 +37,8 @@ export {
36
37
  Accordion,
37
38
  WAccordion,
38
39
  WAccordionItem,
40
+ Breadcumbs,
41
+ WBreadcumbs,
39
42
  Button,
40
43
  WButton,
41
44
  ButtonColor,
@@ -144,6 +144,16 @@ export class Toolbar extends Widget {
144
144
  this.items.set(id, widget);
145
145
  }
146
146
 
147
+ public deleteItem(id: string) {
148
+ const item = this.items.get(id);
149
+
150
+ if (!item) return;
151
+
152
+ item.free();
153
+
154
+ this.items.delete(id);
155
+ }
156
+
147
157
  public init(): void {
148
158
  super.init();
149
159
  }
@@ -28,6 +28,7 @@ import { createDialog, Dialog } from "./dialog";
28
28
  import { createDataGrid, DataGrid } from "./datagrid.ui";
29
29
  import { WidgetAlignTypes, WidgetProps, WidgetTypes } from "./widget.types";
30
30
  import { createIconView } from "./IconView.ui";
31
+ import { createBreadcumbs } from "./breadcumbs.ui";
31
32
 
32
33
  export function createWidget(
33
34
  content: any,
@@ -118,6 +119,8 @@ export function createWidget(
118
119
  widget = createDataGrid(widgetProps.id, content, parent);
119
120
  } else if (content.getAttribute("w-icon-view")) {
120
121
  widget = createIconView(widgetProps.id, content, parent);
122
+ } else if (content.getAttribute("w-breadcumbs")) {
123
+ widget = createBreadcumbs(widgetProps.id, content, parent);
121
124
  } else {
122
125
  widget = new Widget(widgetProps.id, content.tagName, parent);
123
126