rettangoli-ui 0.1.0-rc2 → 0.1.0-rc4

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.
Files changed (54) hide show
  1. package/README.md +16 -27
  2. package/dist/rettangoli-iife-layout.min.js +118 -45
  3. package/dist/rettangoli-iife-ui.min.js +86 -65
  4. package/package.json +11 -4
  5. package/src/common/BaseElement.js +182 -0
  6. package/src/common.js +190 -0
  7. package/src/components/dialog/dialog.handlers.js +5 -0
  8. package/src/components/dialog/dialog.store.js +24 -0
  9. package/src/components/dialog/dialog.view.yaml +41 -0
  10. package/src/components/dropdownMenu/dropdownMenu.handlers.js +5 -0
  11. package/src/components/dropdownMenu/dropdownMenu.store.js +25 -0
  12. package/src/components/dropdownMenu/dropdownMenu.view.yaml +52 -0
  13. package/src/components/form/form.handlers.js +30 -0
  14. package/src/components/form/form.store.js +45 -0
  15. package/src/components/form/form.view.yaml +47 -0
  16. package/src/components/navbar/navbar.examples.yaml +86 -0
  17. package/src/components/navbar/navbar.handlers.js +10 -0
  18. package/src/components/navbar/navbar.store.js +46 -0
  19. package/src/components/navbar/navbar.view.yaml +74 -0
  20. package/src/components/pageOutline/pageOutline.handlers.js +69 -0
  21. package/src/components/pageOutline/pageOutline.store.js +40 -0
  22. package/src/components/pageOutline/pageOutline.view.yaml +34 -0
  23. package/src/components/popover/popover.handlers.js +5 -0
  24. package/src/components/popover/popover.store.js +12 -0
  25. package/src/components/popover/popover.view.yaml +57 -0
  26. package/src/components/select/select.handlers.js +46 -0
  27. package/src/components/select/select.store.js +65 -0
  28. package/src/components/select/select.view.yaml +50 -0
  29. package/src/components/sidebar/sidebar.handlers.js +36 -0
  30. package/src/components/sidebar/sidebar.store.js +139 -0
  31. package/src/components/sidebar/sidebar.view.yaml +190 -0
  32. package/src/entry-iife-layout.js +15 -0
  33. package/src/entry-iife-ui.js +18 -0
  34. package/src/index.js +17 -0
  35. package/src/lib/uhtml.js +9 -0
  36. package/src/primitives/button.js +306 -0
  37. package/src/primitives/image.js +234 -0
  38. package/src/primitives/input.js +208 -0
  39. package/src/primitives/svg.js +95 -0
  40. package/src/primitives/text.js +141 -0
  41. package/src/primitives/textarea.js +103 -0
  42. package/src/primitives/view.js +217 -0
  43. package/src/setup.js +16 -0
  44. package/src/styles/anchorStyles.js +13 -0
  45. package/src/styles/buttonMarginStyles.js +84 -0
  46. package/src/styles/cursorStyles.js +12 -0
  47. package/src/styles/flexChildStyles.js +43 -0
  48. package/src/styles/flexDirectionStyles.js +74 -0
  49. package/src/styles/marginStyles.js +13 -0
  50. package/src/styles/paddingSvgStyles.js +120 -0
  51. package/src/styles/scrollStyles.js +22 -0
  52. package/src/styles/textColorStyles.js +14 -0
  53. package/src/styles/textStyles.js +62 -0
  54. package/src/styles/viewStyles.js +119 -0
@@ -0,0 +1,86 @@
1
+ config: {}
2
+
3
+ ---
4
+ name: 'basic_with_href'
5
+ viewData:
6
+ start:
7
+ label: "My App"
8
+ href: "/"
9
+
10
+ ---
11
+ name: 'with_path_navigation'
12
+ viewData:
13
+ start:
14
+ label: "Dashboard"
15
+ path: "/dashboard"
16
+
17
+ ---
18
+ name: 'with_logo_image'
19
+ viewData:
20
+ start:
21
+ label: "Company Name"
22
+ image:
23
+ src: "/logo.png"
24
+ width: 32
25
+ height: 32
26
+ alt: "Company Logo"
27
+
28
+ ---
29
+ name: 'with_logo_and_href'
30
+ viewData:
31
+ start:
32
+ label: "Rettangoli"
33
+ href: "/"
34
+ image:
35
+ src: "/assets/logo.svg"
36
+ width: 40
37
+ height: 40
38
+ alt: "Rettangoli Logo"
39
+
40
+ ---
41
+ name: 'with_logo_and_path'
42
+ viewData:
43
+ start:
44
+ label: "Project Hub"
45
+ path: "/projects"
46
+ image:
47
+ src: "/icons/hub.png"
48
+ width: 28
49
+ height: 28
50
+ alt: "Hub Icon"
51
+
52
+ ---
53
+ name: 'text_only_no_navigation'
54
+ viewData:
55
+ start:
56
+ label: "Static Header"
57
+
58
+ ---
59
+ name: 'long_label_with_image'
60
+ viewData:
61
+ start:
62
+ label: "Enterprise Resource Planning System"
63
+ image:
64
+ src: "/brand/enterprise-logo.jpg"
65
+ width: 36
66
+ height: 36
67
+ alt: "Enterprise Logo"
68
+
69
+ ---
70
+ name: 'minimal_brand'
71
+ viewData:
72
+ start:
73
+ label: "Brand"
74
+ href: "/home"
75
+
76
+ ---
77
+ name: 'app_with_square_logo'
78
+ viewData:
79
+ start:
80
+ label: "TaskFlow"
81
+ path: "/tasks"
82
+ image:
83
+ src: "/logos/taskflow-square.png"
84
+ width: 30
85
+ height: 30
86
+ alt: "TaskFlow Square Logo"
@@ -0,0 +1,10 @@
1
+
2
+ export const handleClickStart = (e, deps) => {
3
+ const { dispatchEvent, store } = deps;
4
+ console.log('handle click start', store.selectPath());
5
+ dispatchEvent(new CustomEvent('clickStart', {
6
+ detail: {
7
+ path: store.selectPath(),
8
+ }
9
+ }));
10
+ }
@@ -0,0 +1,46 @@
1
+ export const INITIAL_STATE = Object.freeze({});
2
+
3
+ const blacklistedAttrs = ['id', 'class', 'style', 'slot'];
4
+
5
+ const stringifyAttrs = (attrs) => {
6
+ return Object.entries(attrs).filter(([key]) => !blacklistedAttrs.includes(key)).map(([key, value]) => `${key}=${value}`).join(' ');
7
+ }
8
+
9
+ export const toViewData = ({ state, props, attrs }) => {
10
+ console.log('attrs', {
11
+ attrs,
12
+ entries: Object.entries(attrs)
13
+ })
14
+
15
+ const attrsStart = attrs.start ? JSON.parse(decodeURIComponent(attrs.start)) : props.start;
16
+
17
+ const containerAttrString = stringifyAttrs(attrs);
18
+ const start = attrsStart || {
19
+ label: '',
20
+ // href: undefined,
21
+ // path: undefined,
22
+ image: {
23
+ src: '',
24
+ width: 32,
25
+ height: 32,
26
+ alt: ''
27
+ }
28
+ }
29
+ // start.hasImage = start.image && start.image.src;
30
+ // start.hasHref = !!start.href;
31
+ return {
32
+ containerAttrString,
33
+ start
34
+ };
35
+ }
36
+
37
+ export const selectPath = ({ props }) => {
38
+ return props.start.path;
39
+ }
40
+
41
+ export const setState = (state) => {
42
+ // do doSomething
43
+ }
44
+
45
+
46
+
@@ -0,0 +1,74 @@
1
+ elementName: rtgl-navbar
2
+
3
+ viewDataSchema:
4
+ type: object
5
+ properties:
6
+ containerAttrString:
7
+ type: string
8
+ start:
9
+ type: object
10
+ properties:
11
+ label:
12
+ type: string
13
+ href:
14
+ type: string
15
+ image:
16
+ type: object
17
+ properties:
18
+ src:
19
+ type: string
20
+ hasImage:
21
+ type: boolean
22
+ hasHref:
23
+ type: boolean
24
+
25
+ propsSchema:
26
+ type: object
27
+ properties:
28
+ start:
29
+ type: object
30
+ properties:
31
+ label:
32
+ type: string
33
+ href:
34
+ type: string
35
+ image:
36
+ type: object
37
+ properties:
38
+ src:
39
+ type: string
40
+
41
+ refs:
42
+ start:
43
+ eventListeners:
44
+ click:
45
+ handler: handleClickStart
46
+
47
+ events:
48
+ clickStart:
49
+ type: object
50
+ properties:
51
+ path:
52
+ type: string
53
+
54
+ anchors:
55
+ - &image
56
+ - rtgl-text s=lg: "${start.label}"
57
+ - $if start.image && start.image.src:
58
+ - rtgl-image w=${start.image.width} h=${start.image.height} src=${start.image.src} alt=${start.image.alt}:
59
+
60
+ template:
61
+ - rtgl-view bgc=bg d=h h=48 av=c w=f ${containerAttrString}:
62
+ - $if start.href:
63
+ - a href=${start.href}:
64
+ - rtgl-view d=h av=c g=lg g=md:
65
+ - *image
66
+ $elif start.path:
67
+ - rtgl-view#start d=h av=c g=lg g=md cur=p:
68
+ - *image
69
+ $else:
70
+ - rtgl-view d=h av=c g=lg g=md:
71
+ - *image
72
+ - rtgl-view flex=1:
73
+ - rtgl-view d=h av=c g=lg:
74
+ - slot name=right:
@@ -0,0 +1,69 @@
1
+
2
+
3
+ /**
4
+ *
5
+ * @param {*} headingElements
6
+ * @param {*} deps
7
+ */
8
+ const updateToLatestCurrentId = (headingElements, deps) => {
9
+ const { store, render } = deps;
10
+
11
+ let currentHeadingId;
12
+ let closestTopPosition = -Infinity;
13
+
14
+ headingElements.forEach((heading) => {
15
+ const rect = heading.getBoundingClientRect();
16
+
17
+ if (rect.top <= 20) {
18
+ if (rect.top > closestTopPosition) {
19
+ closestTopPosition = rect.top;
20
+ currentHeadingId = heading.id;
21
+ }
22
+ }
23
+ });
24
+
25
+ if (currentHeadingId && currentHeadingId !== store.selectCurrentId()) {
26
+ store.setCurrentId(currentHeadingId);
27
+ render();
28
+ }
29
+ };
30
+
31
+ const startListening = (contentContainer, deps) => {
32
+ const { store, render } = deps;
33
+
34
+ // Extract headings
35
+ const headings = contentContainer.querySelectorAll("rtgl-text[id]");
36
+ const headingElements = Array.from(headings);
37
+
38
+ const items = headingElements.map((heading) => ({
39
+ id: heading.id,
40
+ href: `#${heading.id}`,
41
+ title: heading.textContent
42
+ }));
43
+
44
+ store.setItems(items);
45
+ updateToLatestCurrentId(headingElements, deps);
46
+ render();
47
+
48
+ const boundCheckCurrentHeading = updateToLatestCurrentId.bind(this, headingElements, deps);
49
+
50
+ // Add scroll listener to the content container
51
+ contentContainer.addEventListener("scroll", boundCheckCurrentHeading, {
52
+ passive: true,
53
+ });
54
+
55
+ return () => {
56
+ contentContainer.removeEventListener("scroll", boundCheckCurrentHeading);
57
+ }
58
+ };
59
+
60
+ export const handleOnMount = (deps) => {
61
+ const { attrs } = deps;
62
+ requestAnimationFrame(() => {
63
+ const targetElement = document.getElementById(attrs['target-id'])
64
+ const stopListening = startListening(targetElement, deps)
65
+ return () => {
66
+ stopListening();
67
+ }
68
+ })
69
+ }
@@ -0,0 +1,40 @@
1
+ export const INITIAL_STATE = Object.freeze({
2
+ items: [],
3
+ currentId: null,
4
+ contentContainer: null
5
+ });
6
+
7
+ export const toViewData = ({ state }) => {
8
+ return {
9
+ items: state.items.map((item) => {
10
+ return {
11
+ ...item,
12
+ c: item.id === state.currentId ? 'fg' : 'mu-fg'
13
+ }
14
+ }),
15
+ currentId: state.currentId
16
+ };
17
+ }
18
+
19
+ export const selectState = ({ state }) => {
20
+ return state;
21
+ }
22
+
23
+ export const selectCurrentId = ({ state }) => {
24
+ return state.currentId;
25
+ }
26
+
27
+ export const setItems = (state, items) => {
28
+ state.items = items;
29
+ }
30
+
31
+ export const setCurrentId = (state, id) => {
32
+ state.currentId = id;
33
+ }
34
+
35
+ export const setContentContainer = (state, container) => {
36
+ state.contentContainer = container;
37
+ }
38
+
39
+
40
+
@@ -0,0 +1,34 @@
1
+ elementName: rtgl-page-outline
2
+
3
+ viewDataSchema:
4
+ type: object
5
+ properties:
6
+ items:
7
+ type: array
8
+ items:
9
+ type: object
10
+ properties:
11
+ slug:
12
+ type: string
13
+ title:
14
+ type: string
15
+ selectedSlug:
16
+ type: string
17
+ nullable: true
18
+
19
+ propsSchema:
20
+ type: object
21
+ properties: {}
22
+
23
+ refs: {}
24
+
25
+ events:
26
+ onItemClick:
27
+ type: object
28
+
29
+ template:
30
+ - rtgl-view h=f w=272:
31
+ - rtgl-view w=f g=sm mt=xl:
32
+ - $for item, i in items:
33
+ - rtgl-view pv=xs av=c href=${item.href}:
34
+ - rtgl-text s=sm c=${item.c} h-c=fg: ${item.title}
@@ -0,0 +1,5 @@
1
+
2
+ export const handleClickOverlay = (e, deps) => {
3
+ const { dispatchEvent } = deps;
4
+ dispatchEvent(new CustomEvent('click-overlay'));
5
+ }
@@ -0,0 +1,12 @@
1
+ export const INITIAL_STATE = Object.freeze({});
2
+
3
+ export const toViewData = ({ state, props }) => {
4
+ return {
5
+ isOpen: props.isOpen,
6
+ position: props.position,
7
+ };
8
+ }
9
+
10
+ export const selectState = ({ state }) => {
11
+ return state;
12
+ }
@@ -0,0 +1,57 @@
1
+ elementName: rtgl-popover
2
+
3
+ viewDataSchema:
4
+ type: object
5
+ properties:
6
+ isOpen:
7
+ type: boolean
8
+ position:
9
+ type: object
10
+ properties:
11
+ x:
12
+ type: number
13
+ y:
14
+ type: number
15
+ placement:
16
+ type: string
17
+
18
+ propsSchema:
19
+ type: object
20
+ properties:
21
+ placement:
22
+ type: string
23
+ default: bottom
24
+ isOpen:
25
+ type: boolean
26
+ position:
27
+ type: object
28
+ properties:
29
+ x:
30
+ type: number
31
+ y:
32
+ type: number
33
+
34
+ refs:
35
+ popoverOverlay:
36
+ eventListeners:
37
+ click:
38
+ handler: handleClickOverlay
39
+
40
+ styles:
41
+ '@keyframes popover-in':
42
+ from:
43
+ opacity: 0
44
+ transform: scale(0.95)
45
+ to:
46
+ opacity: 1
47
+ transform: scale(1)
48
+
49
+ '#popoverContainer':
50
+ animation: popover-in 150ms cubic-bezier(0.16, 1, 0.3, 1)
51
+ transform-origin: top
52
+
53
+ template:
54
+ - $if isOpen:
55
+ - rtgl-view#popoverOverlay pos=fix cor=full:
56
+ - 'rtgl-view#popoverContainer pos=fix style="left: ${position.x}px; top: ${position.y}px;" id=floatingElement bw=xs p=md bgc=mu':
57
+ - slot name=content:
@@ -0,0 +1,46 @@
1
+
2
+ export const handleOnMount = (deps) => {
3
+ const { store, props, render } = deps;
4
+
5
+ if (props.selectedValue !== null && props.selectedValue !== undefined && props.options) {
6
+ const selectedOption = props.options.find(opt => opt.value === props.selectedValue);
7
+ if (selectedOption) {
8
+ store.update((state) => {
9
+ state.selectedValue = selectedOption.value;
10
+ state.selectedLabel = selectedOption.label;
11
+ });
12
+ render();
13
+ }
14
+ }
15
+ }
16
+
17
+ export const handleButtonClick = (e, deps) => {
18
+ const { store, render, getRefIds } = deps;
19
+ store.openOptionsPopover({
20
+ position: {
21
+ y: e.clientY,
22
+ x: e.clientX,
23
+ }
24
+ })
25
+ render();
26
+ }
27
+
28
+ export const handleClickOptionsPopoverOverlay = (e, deps) => {
29
+ const { store, render } = deps;
30
+ store.closeOptionsPopover();
31
+ render();
32
+ }
33
+
34
+ export const handleOptionClick = (e, deps) => {
35
+ const { render, dispatchEvent, props } = deps;
36
+ const id = e.currentTarget.id.replace('option-', '');
37
+
38
+ const option = props.options[id];
39
+
40
+ dispatchEvent(new CustomEvent('option-selected', {
41
+ detail: { value: option.value, label: option.label },
42
+ bubbles: true
43
+ }));
44
+
45
+ render();
46
+ }
@@ -0,0 +1,65 @@
1
+ export const INITIAL_STATE = Object.freeze({
2
+ isOpen: false,
3
+ position: {
4
+ x: 0,
5
+ y: 0,
6
+ },
7
+ selectedValue: null,
8
+ selectedLabel: null,
9
+ });
10
+
11
+ export const toViewData = ({ state, props }) => {
12
+ // Calculate display label
13
+ let displayLabel = props.placeholder || 'Select an option';
14
+
15
+ // Use state's selected value if available, otherwise use props.selectedValue
16
+ const currentValue = state.selectedValue !== null ? state.selectedValue : props.selectedValue;
17
+
18
+ if (currentValue !== null && currentValue !== undefined && props.options) {
19
+ const selectedOption = props.options.find(opt => opt.value === currentValue);
20
+ if (selectedOption) {
21
+ displayLabel = selectedOption.label;
22
+ }
23
+ } else if (state.selectedLabel) {
24
+ displayLabel = state.selectedLabel;
25
+ }
26
+
27
+ // Map options to include isSelected flag and computed background color
28
+ const optionsWithSelection = (props.options || []).map(option => ({
29
+ ...option,
30
+ isSelected: option.value === currentValue,
31
+ bgc: option.value === currentValue ? 'mu' : ''
32
+ }));
33
+
34
+ return {
35
+ isOpen: state.isOpen,
36
+ position: state.position,
37
+ options: optionsWithSelection,
38
+ selectedValue: currentValue,
39
+ selectedLabel: displayLabel,
40
+ placeholder: props.placeholder || 'Select an option'
41
+ };
42
+ }
43
+
44
+ export const selectState = ({ state }) => {
45
+ return state;
46
+ }
47
+
48
+ export const openOptionsPopover = (state, payload) => {
49
+ const { position } = payload;
50
+ state.position = position;
51
+ state.isOpen = true;
52
+ }
53
+
54
+ export const closeOptionsPopover = (state) => {
55
+ state.isOpen = false;
56
+ }
57
+
58
+ export const selectOption = (state, option) => {
59
+ state.selectedValue = option.value;
60
+ state.selectedLabel = option.label;
61
+ state.isOpen = false;
62
+ }
63
+
64
+
65
+
@@ -0,0 +1,50 @@
1
+ elementName: rtgl-select
2
+
3
+ viewDataSchema:
4
+ type: object
5
+
6
+ propsSchema:
7
+ type: object
8
+ properties:
9
+ options:
10
+ type: array
11
+ items:
12
+ type: object
13
+ properties:
14
+ id:
15
+ type: string
16
+ label:
17
+ type: string
18
+ value:
19
+ type: any
20
+ selectedValue:
21
+ type: any
22
+ placeholder:
23
+ type: string
24
+ onChange:
25
+ type: function
26
+
27
+ refs:
28
+ select-button:
29
+ eventListeners:
30
+ click:
31
+ handler: handleButtonClick
32
+ popover:
33
+ eventListeners:
34
+ click-overlay:
35
+ handler: handleClickOptionsPopoverOverlay
36
+ option-*:
37
+ eventListeners:
38
+ click:
39
+ handler: handleOptionClick
40
+
41
+ events: {}
42
+
43
+ template:
44
+ - rtgl-button#select-button v=ol:
45
+ - ${selectedLabel}
46
+ - rtgl-popover#popover .isOpen=isOpen .position=position:
47
+ - rtgl-view wh=300 g=xs slot=content bgc=background br=md:
48
+ - $for option, i in options:
49
+ - rtgl-view#option-${i} w=f h-bgc=ac ph=lg pv=md cur=p br=md bgc=${option.bgc}:
50
+ - rtgl-text: ${option.label}
@@ -0,0 +1,36 @@
1
+ export const handleHeaderClick = (e, deps) => {
2
+ const { store, dispatchEvent } = deps;
3
+
4
+ let path;
5
+
6
+ const header = store.selectHeader();
7
+
8
+ if (e.currentTarget.id === 'header-label') {
9
+ path = header.labelPath;
10
+ } else if (e.currentTarget.id === 'header-image') {
11
+ path = header.image.path;
12
+ } else if (e.currentTarget.id === 'header') {
13
+ path = header.path;
14
+ }
15
+
16
+ dispatchEvent(new CustomEvent('headerClick', {
17
+ detail: {
18
+ path
19
+ },
20
+ bubbles: true,
21
+ composed: true
22
+ }));
23
+ }
24
+
25
+ export const handleItemClick = (e, deps) => {
26
+ const { store, dispatchEvent } = deps;
27
+ const id = e.currentTarget.id.replace('item-', '');
28
+ const item = store.selectItem(id);
29
+ dispatchEvent(new CustomEvent('itemClick', {
30
+ detail: {
31
+ item,
32
+ },
33
+ bubbles: true,
34
+ composed: true
35
+ }));
36
+ }