//go:build js && wasm // Package main demonstrates Iris client-side routing capabilities. // // This example shows: // - Router setup with multiple routes // - Route parameters (/users/:id) // - Navigation between pages using Link and Navigate // - Route guards for protected routes // - History management (back/forward) package main import ( "fmt" "git.flowmade.one/flowmade-one/iris/navigation" "git.flowmade.one/flowmade-one/iris/reactive" "git.flowmade.one/flowmade-one/iris/ui" ) // Global auth state to demonstrate route guards var isAuthenticated = reactive.NewSignal(false) func main() { // Define routes with their handlers and guards routes := []navigation.Route{ {Path: "/", Handler: homePage}, {Path: "/about", Handler: aboutPage}, {Path: "/users", Handler: usersPage}, {Path: "/users/:id", Handler: userDetailPage, Guards: []navigation.RouteGuard{ navigation.NumericIdGuard(), }}, {Path: "/admin", Handler: adminPage, Guards: []navigation.RouteGuard{ navigation.AuthGuard(func() bool { return isAuthenticated.Get() }), }}, } // Create router and set up navigation router := navigation.NewRouter(routes) router.SetNotFoundHandler(notFoundPage) navigation.SetGlobalRouter(router) // Mount the app with router support ui.NewAppWithRouter(router) // Keep the application running select {} } // homePage renders the landing page with navigation links func homePage(params map[string]string) ui.View { return pageLayout("Home", ui.VerticalGroup( ui.TextFromString("Welcome to the Iris Multi-Page Demo"). Color("#333"). Padding("16px"), ui.TextFromString("This example demonstrates client-side routing with:"). Color("#666"). Padding("8px"), ui.VerticalGroup( ui.TextFromString("- Multiple routes and page navigation").Color("#888"), ui.TextFromString("- Route parameters (see /users/:id)").Color("#888"), ui.TextFromString("- Route guards for protected pages").Color("#888"), ui.TextFromString("- Browser history integration").Color("#888"), ).Padding("8px 24px"), ui.TextFromString("Use the navigation bar above to explore."). Color("#666"). Padding("16px"), ), ) } // aboutPage renders information about the demo func aboutPage(params map[string]string) ui.View { return pageLayout("About", ui.VerticalGroup( ui.TextFromString("About Iris Navigation"). Color("#333"). Padding("16px"), ui.TextFromString("The navigation package provides:"). Color("#666"). Padding("8px"), ui.VerticalGroup( ui.TextFromString("Router - Define routes with path patterns and handlers").Color("#888"), ui.TextFromString("RouteGuard - Protect routes with custom logic").Color("#888"), ui.TextFromString("HistoryManager - Integrate with browser history").Color("#888"), ui.TextFromString("Link - Create navigational elements").Color("#888"), ui.TextFromString("Navigate/Back/Forward - Programmatic navigation").Color("#888"), ).Padding("8px 24px"), ), ) } // usersPage renders a list of users with links to their detail pages func usersPage(params map[string]string) ui.View { users := []struct { ID string Name string }{ {"1", "Alice"}, {"2", "Bob"}, {"3", "Charlie"}, {"4", "Diana"}, } var userLinks []ui.View for _, user := range users { // Capture user in closure u := user userLinks = append(userLinks, navigation.Link(fmt.Sprintf("/users/%s", u.ID), ui.TextFromString(fmt.Sprintf("View %s (ID: %s)", u.Name, u.ID)). Color("#0066cc"), ).Padding("8px").Cursor("pointer"), ) } return pageLayout("Users", ui.VerticalGroup( ui.TextFromString("User Directory"). Color("#333"). Padding("16px"), ui.TextFromString("Click a user to see their details:"). Color("#666"). Padding("8px"), ui.VerticalGroup(userLinks...).Padding("8px"), ), ) } // userDetailPage shows details for a specific user using the :id parameter func userDetailPage(params map[string]string) ui.View { userID := params["id"] // Simulated user data userData := map[string]struct { Name string Email string Role string }{ "1": {"Alice", "alice@example.com", "Admin"}, "2": {"Bob", "bob@example.com", "User"}, "3": {"Charlie", "charlie@example.com", "User"}, "4": {"Diana", "diana@example.com", "Moderator"}, } user, exists := userData[userID] if !exists { return pageLayout("User Not Found", ui.VerticalGroup( ui.TextFromString(fmt.Sprintf("User with ID %s not found", userID)). Color("#ff4444"). Padding("16px"), ui.Button(func() { navigation.Navigate("/users") }, ui.TextFromString("Back to Users")). Padding("8px"), ), ) } return pageLayout(fmt.Sprintf("User: %s", user.Name), ui.VerticalGroup( ui.TextFromString(fmt.Sprintf("User Details (ID: %s)", userID)). Color("#333"). Padding("16px"), ui.VerticalGroup( ui.TextFromString(fmt.Sprintf("Name: %s", user.Name)).Color("#666"), ui.TextFromString(fmt.Sprintf("Email: %s", user.Email)).Color("#666"), ui.TextFromString(fmt.Sprintf("Role: %s", user.Role)).Color("#666"), ).Padding("8px 24px"), ui.HorizontalGroup( ui.Button(func() { navigation.Back() }, ui.TextFromString("Go Back")).Padding("8px"), ui.Button(func() { navigation.Navigate("/users") }, ui.TextFromString("All Users")).Padding("8px"), ).Padding("16px"), ), ) } // adminPage is a protected route that requires authentication func adminPage(params map[string]string) ui.View { return pageLayout("Admin", ui.VerticalGroup( ui.TextFromString("Admin Dashboard"). Color("#333"). Padding("16px"), ui.TextFromString("Welcome to the protected admin area!"). Color("#28a745"). Padding("8px"), ui.TextFromString("This page is protected by an AuthGuard."). Color("#666"). Padding("8px"), ui.Button(func() { isAuthenticated.Set(false) navigation.Navigate("/") }, ui.TextFromString("Logout")). Padding("8px"). Background("#dc3545"). Foreground("#fff"), ), ) } // notFoundPage renders when no route matches func notFoundPage() ui.View { return pageLayout("404", ui.VerticalGroup( ui.TextFromString("404 - Page Not Found"). Color("#ff4444"). Padding("16px"), ui.TextFromString("The page you are looking for does not exist."). Color("#666"). Padding("8px"), ui.Button(func() { navigation.Navigate("/") }, ui.TextFromString("Go Home")).Padding("8px"), ), ) } // pageLayout provides consistent page structure with navigation func pageLayout(title string, content ui.View) ui.View { return ui.VerticalGroup( navBar(), ui.VerticalGroup( ui.TextFromString(title). Color("#333"). Padding("8px"). Background("#f0f0f0"). Width("100%"), content, ).Padding("16px"), ).MinHeight("100vh") } // navBar creates the navigation header with links func navBar() ui.View { return ui.HorizontalGroup( navigation.Link("/", ui.TextFromString("Home").Color("#fff")), navigation.Link("/about", ui.TextFromString("About").Color("#fff")), navigation.Link("/users", ui.TextFromString("Users").Color("#fff")), authButton(), ).Background("#333").Padding("8px 16px").AlignItems("center") } // authButton shows login/admin based on auth state func authButton() ui.View { return ui.Button(func() { if isAuthenticated.Get() { navigation.Navigate("/admin") } else { isAuthenticated.Set(true) navigation.Navigate("/admin") } }, ui.TextFromFunction(func() string { if isAuthenticated.Get() { return "Admin" } return "Login" }).Color("#fff")).Background("transparent").Border("1px solid #fff").Padding("4px 12px").Cursor("pointer") }