Beispiele

Individuelles Design in der CalendarWeek

Individuelles Design in der CalendarWeek

In diesem Blogbeitrag bauen wir step-by-step, mithilfe von Layouts und weiteren Widgets, eine individuelle Card, die in der customCalendarWeek eingesetzt wird.

Ihr findet hier Beispiele, wie die Integration von Widgets innerhalb von anderen Widgets funktionieren kann und die Anwendungscodes, die wir für dieses Setup erstellt haben.

Entweder als Vorlage zum direkt nutzen (natürlich müsst ihr noch Tabellennamen, Felder und Ids mit euren ersetzen) oder ihr nehmt Teile davon, passt sie ganz arcRiderWidget like nach euren Vorstellungen an bzw. ersetzt sie mit euren eigenen Widget Designs.

Widget: customCalendarWeek
Integrierte Mini Widgets:
Layout, Button, Select, Icon, Image und Text.

Let´s build together

Zur Vorbereitung erstellt ihr die Ninox Tabellen und Felder, die ihr braucht (wenn ihr nicht schon Vorhandene in eurer Datenbank habt). Folgende Tabellen werden für das Layout der Card in diesem UseCase genutzt:

Tipp: Für die die Dynamische Auswahl können Hilfs/Settings Tabellen erstellt werden, wo Farb- und Iconwerte hinterlegt und dann in den jeweiligen Widgets aufgerufen werden können.

So sind die Werte zentral und müssen nicht immer wieder manuell eingegeben werden, wenn man sie auch auf anderen Oberflächen in verschiedenen Widgets einsetzen möchte.  

Individuelles Design der Card

Widgets übersichtlich integrieren

Das Layout der calenderCard und die einzelnen zu integrierenden Widgets, werden in der Tabelle Termine erstellt, deren Records zum Schluss im customCalendarWeek aufgerufen werden.

Hier erstellen wir für jede Zeile der Card ein fx-Feld mit je einem Layout und fx-Felder für Elemente, wie weitere Layouts, Select, Button. Das bietet gute Übersichtlichkeit beim Coden der Layouts, außerdem können verschiedene Designs schneller ausgetauscht/ersetzt/getestet werden.

 Line 1

Schritt 1: Wir beginnen mit den ausgelagerten Elementen für die erste Zeile.

select_status

Verwendete Widgets: customSelect + customLayout

Code:

let current := this;
arcCustomSelect({
		uniqueId: "Status Termin" + nr,
		recordId: nr,
		field: "Y",
		embedded: true,
		width: "100%",
		height: "25px",
		alignX: "",
		fontColor: "",
		backgroundColor: text(record(Status_Termin,number(Status)).Farbcode),
		borderColor: "none",
		borderWidth: "0px",
		borderRadius: "5px",
		itemsSettings: {
			width: "100%",
			height: "auto",
			borderRadius: "5px"
		},
		clickPosition: "",
		placeholder: "",
		disabled: false,
		reset: {
			title: "reset",
			value: "",
			hide: true
		},
		currentValue: arcCustomLayout({
				uniqueId: "status-bezeichnung-current " + nr,
				embedded: true,
				direction: "horizontal",
				alignX: "left",
				alignY: "center",
				width: "100%",
				height: "auto",
				gap: "10px",
				backgroundColor: text(record(Status_Termin,number(Status)).Farbcode),
				paddingY: "1px",
				paddingX: "1px",
				styles: "border-radius: 5px;",
				scrollSettings: {
					scrollY: false,
					scrollX: false
				},
				blocks: [{
						width: "100%",
						height: "auto",
						lineHeight: "",
						alignX: "left",
						color: "",
						styles: "",
						value: text(record(Status_Termin,number(Status)).Bezeichnung)
					}]
			}),
		items: let id := nr;
		(select Status_Termin).[{
				title: arcCustomLayout({
						uniqueId: "status-bezeichnung " + Nr,
						embedded: true,
						direction: "horizontal",
						alignX: "left",
						alignY: "center",
						width: "100%",
						height: "",
						gap: "0px",
						backgroundColor: Farbcode,
						paddingY: "3px",
						paddingX: "5px",
						styles: "border-radius: 5px;",
						scrollSettings: {
							scrollY: false,
							scrollX: false
						},
						blocks: [{
								width: "100%",
								height: "auto",
								lineHeight: "",
								alignX: "left",
								color: "",
								styles: "",
								value: Bezeichnung
							}]
					}),
				active: number(id.Status) = number(Nr),
				value: number(Nr)
			}]
	})

layout_line1_left

Verwendete Widgets: customLayout + customIcon + customText

Code:

let current := this;
arcCustomLayout({
		uniqueId: "calenderCard line1 left" + nr,
		embedded: true,
		direction: "horizontal",
		alignX: "center",
		alignY: "center",
		width: "100%",
		height: "100%",
		gap: "3px",
		backgroundColor: "",
		paddingY: "0px",
		paddingX: "0px",
		styles: "",
		scrollSettings: {
			scrollY: false,
			scrollX: false
		},
		blocks: [{
				width: "auto",
				height: "auto",
				alignX: "center",
				color: "",
				styles: "",
				clickAction: {
					recordId: nr,
					type: "popup"
				},
				value: arcCustomIcon({
						iconSet: "phosphor",
						name: text(record(Typ_Termin,number(Typ)).iconName),
						containerSize: "20px",
						iconSize: "20px",
						color: text(record(Typ_Termin,number(current.Typ)).colorDark)
					})
			}, {
				width: "100%",
				height: "100%",
				alignX: "left",
				color: "",
				clickAction: {
					recordId: nr,
					type: "popup"
				},
				value: arcCustomText({
						uniqueId: "appointment-title" + nr,
						embedded: true,
						fontSize: "12px",
						fontWeight: "700",
						fontColor: text(record(Typ_Termin,number(current.Typ)).colorDark),
						lineHeight: "1.2",
						textDirection: "horizontal",
						value: text(Bezeichnung)
					})
			}]
	})

Schritt 2: Dann werden beide Elemente in das Layout für die erste Zeile gesetzt.

calendarCard_line1

Verwendete Widgets: customLayout

Code:

let current := this;
arcCustomLayout({
		uniqueId: "calenderCard line1 " + nr,
		embedded: true,
		direction: "horizontal",
		alignX: "center",
		alignY: "top",
		width: "100%",
		height: "100%",
		gap: "10px",
		backgroundColor: "",
		paddingY: "0px",
		paddingX: "0px",
		styles: "",
		scrollSettings: {
			scrollY: false,
			scrollX: false
		},
		blocks: [{
				width: "fraction",
				height: "auto",
				alignX: "center",
				color: "",
				value: layout_line1_left
			}, {
				width: "35%",
				height: "auto",
				alignX: "right",
				color: "",
				value: select_status
			}]
	})

Line 2

Schritt 1: Wir beginnen wieder mit dem ausgelagerten Element für die zweite Zeile.

button_customer

Verwendete Widgets: customButton + customIcon

Code:

let current := this;
let lilaDark := "#35339E";
let lilaMiddle := "#D2D2F7";
arcCustomButton({
		uniqueId: "Button-kunde " + nr,
		title: text(Kunde.Name),
		embedded: true,
		width: "auto",
		height: "25px",
		alignY: "",
		alignX: "",
		paddingX: "",
		paddingY: "",
		gap: "5px",
		icon: arcCustomIcon({
				name: "link",
				color: lilaDark,
				containerSize: "20px",
				borderSize: "",
				borderColor: "",
				borderRadius: "",
				iconSize: "20px"
			}),
		iconPosition: "left",
		fontSize: "12px",
		fontColor: lilaDark,
		backgroundColor: lilaMiddle,
		borderColor: lilaMiddle,
		borderRadius: "5px",
		showBadge: false,
	})

Schritt 2: Dann wird das Element in das Layout in die zweite Zeile eingesetzt.

calendarCard_line2

Verwendete Widgets: customLayout + customText

Code:

let current := this;
arcCustomLayout({
		uniqueId: "calenderCard line2 " + nr,
		embedded: true,
		direction: "horizontal",
		alignX: "center",
		alignY: "top",
		width: "100%",
		height: "100%",
		gap: "10px",
		backgroundColor: "",
		paddingY: "",
		paddingX: "",
		styles: "",
		scrollSettings: {
			scrollY: false,
			scrollX: false
		},
		blocks: [{
				width: "50%",
				height: "auto",
				lineHeight: "",
				alignX: "left",
				color: "",
				styles: "",
				value: arcCustomText({
						uniqueId: "appointment-time" + nr,
						embedded: true,
						fontSize: "15px",
						fontWeight: "500",
						fontColor: text(record(Typ_Termin,number(current.Typ)).colorDark),
						lineHeight: "1.2",
						textDirection: "",
						value: text(Von) + " - " + text(Bis) + " Uhr"
					})
			}, {
				width: "50%",
				height: "auto",
				lineHeight: "",
				alignX: "right",
				color: "",
				value: if Kunde != null then button_customer end,
				clickAction: {
					type: "popup",
					recordId: current.Kunde.Nr
				}
			}]
	})

Line 3 

Schritt 1: Die dritte Zeile ist in zwei Layouts aufgeteilt, außerdem brauchen wir ein fx-Feld für die Berechnung der auf 3 Teilnehmer begrenzte Anzeige.

paginatedList_teilnehmer

Code:

let maxEntries := 3;
let currentPage := 1;
let list := Terminteilnehmer;
let paginatedList := unique(record(Terminteilnehmer,0).Mitarbeiter)[!= 0];
for listItem in slice(list.Nr, maxEntries * (currentPage - 1), maxEntries * currentPage) do
	paginatedList := unique(paginatedList, record(Terminteilnehmer,listItem).Mitarbeiter)
end;
paginatedList

layout_buttons

Verwendete Widgets: customLayout + customButton + customIcon

Code:

let current := this;
arcCustomLayout({
		uniqueId: "layout-buttons " + nr,
		embedded: true,
		direction: "horizontal",
		alignX: "right",
		alignY: "center",
		width: "100%",
		height: "auto",
		gap: "5px",
		backgroundColor: "",
		paddingY: "0px",
		paddingX: "0px",
		styles: "",
		scrollSettings: {
			scrollY: false,
			scrollX: false
		},
		blocks: [{
				width: "auto",
				height: "auto",
				lineHeight: "",
				alignX: "left",
				color: "",
				styles: "",
				value: arcCustomButton({
						uniqueId: "Button-paperclip " + nr,
						title: "",
						width: "25px",
						height: "25px",
						alignY: "",
						alignX: "",
						paddingX: "",
						paddingY: "",
						gap: "5px",
						icon: arcCustomIcon({
								name: "paperclip-fill",
								color: text(record(Typ_Termin,number(current.Typ)).colorDark),
								containerSize: "25px",
								borderSize: "",
								borderColor: "",
								borderRadius: "",
								iconSize: "20px"
							}),
						iconPosition: "left",
						fontSize: "12px",
						fontColor: "",
						backgroundColor: "none",
						borderColor: "none",
						borderRadius: "5px",
						showBadge: false,
						actions: [{
								type: "create",
								tableId: "",
								popup: true,
								changeFieldValues: [{
										fieldId: "",
										value: ""
									}, {
										fieldId: "",
										value: ""
									}]
							}]
					})
			}, {
				width: "auto",
				height: "auto",
				lineHeight: "",
				alignX: "left",
				color: "",
				styles: "",
				value: arcCustomButton({
						uniqueId: "Button-message " + nr,
						title: "",
						width: "25px",
						height: "25px",
						alignY: "",
						alignX: "",
						paddingX: "",
						paddingY: "",
						gap: "5px",
						icon: arcCustomIcon({
								name: "chat-circle-dots-fill",
								color: text(record(Typ_Termin,number(current.Typ)).colorDark),
								containerSize: "25px",
								borderSize: "",
								borderColor: "",
								borderRadius: "",
								iconSize: "20px"
							}),
						iconPosition: "left",
						fontSize: "12px",
						fontColor: "",
						backgroundColor: "none",
						borderColor: "none",
						borderRadius: "5px",
						showBadge: false,
						actions: [{
								type: "",
								tableId: "",
								popup: true,
								changeFieldValues: [{
										fieldId: "",
										value: ""
									}, {
										fieldId: "",
										value: ""
									}]
							}]
					})
			}, {
				width: "auto",
				height: "",
				lineHeight: "",
				alignX: "left",
				color: "",
				styles: "",
				value: arcCustomButton({
						uniqueId: "Button-list " + nr,
						title: "",
						width: "25px",
						height: "25px",
						alignY: "",
						alignX: "",
						paddingX: "",
						paddingY: "",
						gap: "5px",
						icon: arcCustomIcon({
								name: "list-checks-fill",
								color: text(record(Typ_Termin,number(current.Typ)).colorDark),
								containerSize: "25px",
								borderSize: "",
								borderColor: "",
								borderRadius: "",
								iconSize: "20px"
							}),
						iconPosition: "left",
						fontSize: "12px",
						fontColor: "",
						backgroundColor: "none",
						borderColor: "none",
						borderRadius: "5px",
						showBadge: false,
						actions: [{
								type: "",
								tableId: "",
								popup: true,
								changeFieldValues: [{
										fieldId: "",
										value: ""
									}, {
										fieldId: "",
										value: ""
									}]
							}]
					})
			}]
	})

layout_teilnehmer

Verwendete Widgets: customLayout + customImage

Code:

let current := this;
let colorDark := text(record(Typ_Termin,number(current.Typ)).colorDark);
let colorLight := text(record(Typ_Termin,number(current.Typ)).color);
arcCustomLayout({
		uniqueId: "layout-teilnehmer " + nr,
		embedded: true,
		direction: "horizontal",
		alignX: "left",
		alignY: "center",
		width: "100%",
		height: "",
		gap: "0px",
		backgroundColor: "",
		paddingY: "0px",
		paddingX: "0px",
		styles: "",
		scrollSettings: {
			scrollY: false,
			scrollX: false
		},
		blocks: let imageList := paginatedList_teilnehmer.[{
					width: "auto",
					height: "auto",
					lineHeight: "",
					alignX: "left",
					color: "",
					styles: "margin-right: -10px; border: 2px solid; border-radius: 50px; border-color: " +
					colorLight +
					";",
					value: arcCustomImage({
							uniqueId: "Mitarbeier-image" + Nr,
							title: "Logo",
							width: "30px",
							height: "30px",
							backgroundSize: "cover",
							class: "",
							style: "",
							radius: "50px",
							borderColor: "",
							link: shareURL
						}),
					clickAction: {
						type: "popup",
						recordId: Nr
					}
				}];
		let plusBubble := [if cnt(Terminteilnehmer) > 3 then
					{
						width: "30px",
						height: "30px",
						lineHeight: "",
						alignX: "center",
						color: colorDark,
						styles: "margin-left: 20px; border: 1px solid; border-radius: 50px; border-color: " +
						colorDark +
						"; font-size: 12px;",
						value: "+" + if cnt(Terminteilnehmer) > 3 then
							text(cnt(Terminteilnehmer) - 3)
						end
					}
				end];
		array(imageList, plusBubble)
	})

Schritt 2: Als nächstes werden beide customLayouts in das Layout der dritten Zeile eingesetzt.

calendarCard_line3

Verwendete Widgets: customLayout

Code:

let current := this;
arcCustomLayout({
		uniqueId: "calenderCard Line3 " + nr,
		embedded: true,
		direction: "horizontal",
		alignX: "center",
		alignY: "center",
		width: "",
		height: "auto",
		gap: "10px",
		backgroundColor: "",
		paddingY: "",
		paddingX: "",
		styles: "",
		scrollSettings: {
			scrollY: false,
			scrollX: false
		},
		blocks: [{
				width: "100%",
				height: "auto",
				lineHeight: "",
				alignX: "left",
				color: "",
				styles: "",
				value: layout_teilnehmer
			}, {
				width: "100%",
				height: "auto",
				lineHeight: "",
				alignX: "right",
				color: "",
				value: layout_buttons
			}]
	})

Card Design zusammenfügen

Abschließend können die Zeilen im letzten Layout der calendarCard zusammengefügt werden. Damit die Card bei kürzeren Terminen noch vollständig angeschaut werden kann, denkt dran in den scrollSettings, scrollY: true zu setzen.                

Verwendete Widgets: customLayout

Code:

let current := this;
arcCustomLayout({
		uniqueId: "calenderCard " + nr,
		embedded: true,
		direction: "vertical",
		alignX: "center",
		alignY: "top",
		width: "100%",
		height: "100%",
		gap: "10px",
		backgroundColor: record(Typ_Termin,number(current.Typ)).color,
		paddingY: "8px",
		paddingX: "8px",
		styles: "border-radius: 14px;",
		scrollSettings: {
			scrollY: true,
			scrollX: false
		},
		blocks: [{
				width: "100%",
				height: "auto",
				lineHeight: "",
				alignX: "center",
				color: "",
				styles: "",
				value: calendarCard_line1
			}, {
				width: "100%",
				height: "100%",
				lineHeight: "",
				alignX: "center",
				color: "",
				value: calendarCard_line2
			}, {
				width: "100%",
				height: "auto",
				lineHeight: "",
				alignX: "center",
				color: "",
				value: calendarCard_line3
			}]
	})

Tipp: Durch die Auslagerung der einzelnen Elemente, könnt ihr super schnell weitere Designs erstellen und andere Ansichten erschaffen. Zum Beispiel die Zeilen tauschen:

Code:

let current := this;
arcCustomLayout({
		uniqueId: "calenderCard version 2" + nr,
		embedded: true,
		direction: "vertical",
		alignX: "center",
		alignY: "top",
		width: "100%",
		height: "100%",
		gap: "10px",
		backgroundColor: record(Typ_Termin,number(current.Typ)).color,
		paddingY: "8px",
		paddingX: "8px",
		styles: "border-radius: 14px;",
		scrollSettings: {
			scrollY: true,
			scrollX: false
		},
		blocks: [{
				width: "100%",
				height: "auto",
				lineHeight: "",
				alignX: "center",
				color: "",
				styles: "",
				value: calendarCard_line2
			}, {
				width: "100%",
				height: "100%",
				lineHeight: "",
				alignX: "center",
				color: "",
				value: calendarCard_line1
			}, {
				width: "100%",
				height: "auto",
				lineHeight: "",
				alignX: "center",
				color: "",
				value: calendarCard_line3
			}]
	})

Finish Design

Die fertige Card kann Abschließend im Widget customCalendarWeek eingesetzt werden. Das Layout unserer calendarCard wird bei den timeEntries im Parameter customLayout aufgerufen.

...		timeEntries: (select Termine where forCalendarWeek = true).[{
				subtitle: "",
				title: "",
				value: "",
  				customLayout: calendarCard,
                                                                ...

Wichtig! Wenn ihr die Hintergrundfarbe und einen borderRadius im Layout der Card definiert, müsst ihr dran denken, die backgroundColor bei den styles Parameter der timeEntries auf "transparent" sowie padding auf "0px" zu stellen. 

...		styles: {
			backgroundColor: "transparent",
            padding: "0px"
				},
                                                                ...

Vollständiger Anwendungscode der customCalendarWeek:

let current := this;
arcCustomCalendarWeek({
		uniqueId: "customCalendar" + Nr,
		embedded: false,
		height: "800px",
		weekdaySettings: {
			week: week(today()),
			year: year(today())
		},
		weekStart: 1,
		timeZoneBalance: -1,
		timeSettings: {
			timeStart: time(6, 30),
			duration: time(15, 30)
		},
		currentTime: "#f95757",
		dayHighlights: [{
				date: date(2024, 10, 31),
				color: ""
			}, {
				weekday: 6,
				color: "#f9f9f9"
			}, {
				weekday: 7,
				color: "#f9f9f9"
			}],
		lang: clientLang(),
		timeSlotWidth: "372px",
		timeSlotHeight: "120px",
		styles: {
			backgroundColor: "#cccccc11"
		},
		timeEntries: (select Termine where forCalendarWeek = true).[{
				subtitle: "",
				title: "",
				value: "",
				customLayout: calendarCard,
				dateFrom: 'Datum von',
				dateTo: 'Datum bis',
				timeFrom: Von,
				timeTo: Bis,
				styles: {
					backgroundColor: "transparent",
                    padding: "0px"
				},
				dragAction: {
					recordId: nr,
					dateFrom: fieldId(nr, "Datum von"),
					timeFrom: fieldId(nr, "Von"),
					dateTo: fieldId(nr, "Datum bis"),
					timeTo: fieldId(nr, "Bis")
				}
			}]
	})