Search

Support

Discord

English

Search

Support

Discord

English

Examples

Custom design in the Calendar Week

Customized Design in the CalendarWeek

Bildschirm mit farbigem Wochenkalender und verschiedenen Terminen; Text: „Calendar {individuell gestaltet}“ auf gelbem Hintergrund. Screen with a colorful weekly calendar and various appointments; text: "Calendar {individually designed}" on a yellow background.

In this article, we will show you step by step how to create a custom-designed Card for the customCalendarWeek using Layouts and additional Widgets.

💡 You will learn not only how widgets can be neatly nested, but you will also receive the appropriate example codes from our setup right away.

Whether as a direct template (you will need to adjust table names, fields, and IDs to your system, of course) – or as inspiration for your own designs in the typical arcRiderWidget style: This guide will help you take your calendar to the next level both visually and functionally.

Required Widgets

Let’s build together 🚀

Before we get started, please ensure that your Ninox database contains the necessary tables and fields – you can either use existing structures or create new ones.

For the layout of the card in this use case, we use the following tables:


Tables

  • Customers

  • Employees

    • Appointments

    • Appointment Participants

  • Custom Calendar

    • Status_Appointments

    • Type_Appointment

Tip: For the Dynamic Selection, auxiliary/settings tables can be created where color and icon values are stored and then called in the respective widgets.

This way, the values are centralized and do not have to be manually entered repeatedly when you want to use them in other interfaces across different widgets.  

Customized Design of the Card

Integrate Widgets Clearly

The layout of the calendarCard and the individual widgets to be integrated are created in the Appointments table, whose records are ultimately called in the customCalendarWeek.

Here we create an fx-field for each row of the card with a layout and fx-fields for elements such as additional layouts, select, button. This provides good clarity when coding the layouts; moreover, different designs can be exchanged/replaced/tested more quickly.

 Line 1

Step 1: We start with the external elements for the first row.

select_status

Used 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

Used 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)
					})
			}]
	})

Step 2: Then both elements are placed into the layout for the first row.

calendarCard_line1

Used 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

Step 1: We start again with the external element for the second row.

button_customer

Used 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,
	})

Step 2: Then the element is placed in the layout in the second row.

calendarCard_line2

Used 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 

Step 1: The third row is divided into two layouts; additionally, we need an fx-field for the calculation of the display limited to 3 participants.

paginatedList_participants

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

Used 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_participants

Used 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)
	})

Step 2: Next, both customLayouts are placed into the layout of the third row.

calendarCard_line3

Used 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
			}]
	})

Combine Card Design

Finally the rows can be combined in the last layout of the calendarCard. To ensure that the card can still be fully viewed during shorter appointments, remember to set scrollSettings, scrollY: true.                

Used 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
			}]
	})

Tip: By externalizing individual elements, you can quickly create additional designs and generate different views. For example, swap the rows:

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

The finished card can be finally used in the customCalendarWeek widget. The layout of our calendarCard is called in the timeEntries using the parameter customLayout.

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

Important! When you define the background color and a borderRadius in the layout of the card, remember to set the backgroundColor in the styles parameter of the timeEntries to "transparent" and padding to "0px". 

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

Full Application Code of the 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")
				}
			}]
	})

 

Arc Rider Ventures GmbH

© 2025

Arc Rider Ventures GmbH

© 2025