diff --git a/examples/gno.land/p/demo/uif/gno.mod b/examples/gno.land/p/demo/uif/gno.mod new file mode 100644 index 00000000000..a40f3a89ace --- /dev/null +++ b/examples/gno.land/p/demo/uif/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/uif diff --git a/examples/gno.land/p/demo/uif/uif.gno b/examples/gno.land/p/demo/uif/uif.gno new file mode 100644 index 00000000000..d08a4db7e59 --- /dev/null +++ b/examples/gno.land/p/demo/uif/uif.gno @@ -0,0 +1,380 @@ +package uif + +import ( + "strconv" + "strings" +) + +// Theme structure and default theme +type Theme struct { + PrimaryColor string + SecondaryColor string + FontSize string + BorderRadius string + DefaultPadding string + DefaultMargin string +} + +func DefaultTheme() Theme { + return Theme{ + PrimaryColor: "#3498db", // Blue color + SecondaryColor: "#2ecc71", // Green color + FontSize: "18px", // Slightly larger font size + BorderRadius: "8px", // More rounded corners + DefaultPadding: "12px 24px", // Custom padding for better spacing + DefaultMargin: "10px", // Default margin + } +} + +// Theme method for Navbar +func (theme Theme) Navbar(links map[string]string) string { + nav := `` + return nav +} + +// Margin wraps content with customizable margin +func (theme Theme) Margin(content string, margin string) string { + if margin == "" { + margin = theme.DefaultMargin // Use default margin + } + return `
` + content + `
` +} + +// Padding wraps content with customizable padding +func (theme Theme) Padding(content string, padding string) string { + if padding == "" { + padding = theme.DefaultPadding // Use default padding + } + return `
` + content + `
` +} + +// Theme method for Button +func (theme Theme) Button(text, href string) string { + return "\n" + `` + text + `` + "\n" +} + +// Theme method for Image with customizable width and height +func (theme Theme) Image(src, width, height string) string { + // Set default width and height if not provided + if width == "" { + width = "100%" + } + if height == "" { + height = "auto" + } + return `Sample Image` +} + +// Theme method for Card with customizable width and height +func (theme Theme) Card(content, width, height string) string { + // Set default width and height if not provided + if width == "" { + width = "100%" + } + if height == "" { + height = "auto" + } + return `
` + content + `
` +} + +// Theme method for TextInput with name and placeholder +func (theme Theme) TextInput(name, placeholder string) string { + return `
+ + +
` +} + +// Theme method for CodeBlock +func (theme Theme) CodeBlock(code string) string { + return `
` + code + `
+` +} + +// Theme method for Grid +func (theme Theme) Grid(items []string, columns int, gap string, textAlign string) string { + columnsStr := strconv.Itoa(columns) + grid := `
` + for _, item := range items { + grid += `
` + item + `
` + } + grid += `
` + return grid +} + +// Theme method for Footer +func (theme Theme) Footer(links []string) string { + footer := `` + return footer +} + +// Theme method for Heading with adjustable size and margin based on level +func (theme Theme) Heading(level int, text string) string { + if level < 1 || level > 6 { + level = 1 + } + tag := "h" + strconv.Itoa(level) + var fontSize, margin string + switch level { + case 1: + fontSize = "34px" + margin = "24px 0" + case 2: + fontSize = "30px" + margin = "18px 0" + case 3: + fontSize = "24px" + margin = "16px 0" + case 4: + fontSize = "20px" + margin = "14px 0" + case 5: + fontSize = "18px" + margin = "12px 0" + case 6: + fontSize = "16px" + margin = "10px 0" // Smallest font for h6 + default: + fontSize = theme.FontSize + margin = theme.DefaultMargin + } + return `<` + tag + ` style=' +font-size: ` + fontSize + `; +color: ` + theme.PrimaryColor + `; +margin: ` + margin + `; +'>` + text + `` +} + +func (theme Theme) Paragraph(text string) string { + return "\n" + `

` + text + `

` + "\n" +} + +// Theme method for Divider +func (theme Theme) Divider() string { + return `
` +} + +// Theme method for Container with customizable width, height, and background +func (theme Theme) Container(content, width, height, background string) string { + // Set default values if not provided + if width == "" { + width = "100%" + } + if height == "" { + height = "auto" + } + if background == "" { + background = "transparent" + } + return `
` + content + `
` +} + +// Flexbox alignment for content with customizable horizontal and vertical alignment +func (theme Theme) Align(content, align string) string { + // Set default alignment to left if not provided + if align == "" { + align = "left" + } + return `
` + content + `
` +} + +// Theme method for Form with action URL, input fields, and submit button +func (theme Theme) Form(fields []string, actionURL string, realmFunc string, submitText string) string { + formTitle := `

` + realmFunc + `

` + return formTitle + `
+ + +` + strings.Join(fields, "\n") + ` + +
` +} + +// Standalone functions for default theme components +func Form(fields []string, actionURL string, realmFunc string, submitText string) string { + var theme Theme = DefaultTheme() + return theme.Form(fields, actionURL, realmFunc, submitText) +} + +func Navbar(links map[string]string) string { + var theme Theme = DefaultTheme() + return theme.Navbar(links) +} + +func Button(text, href string) string { + var theme Theme = DefaultTheme() + return theme.Button(text, href) +} + +func Image(src, width, height string) string { + var theme Theme = DefaultTheme() + return theme.Image(src, width, height) +} + +func Card(content, width, height string) string { + var theme Theme = DefaultTheme() + return theme.Card(content, width, height) +} + +func TextInput(name, placeholder string) string { + var theme Theme = DefaultTheme() + return theme.TextInput(name, placeholder) +} + +func CodeBlock(code string) string { + var theme Theme = DefaultTheme() + return theme.CodeBlock(code) +} + +func Grid(items []string, columns int, gap string, textAlign string) string { + var theme Theme = DefaultTheme() + return theme.Grid(items, columns, gap, textAlign) +} + +func Footer(links []string) string { + var theme Theme = DefaultTheme() + return theme.Footer(links) +} + +func Heading(level int, text string) string { + var theme Theme = DefaultTheme() + return theme.Heading(level, text) +} + +func Paragraph(text string) string { + var theme Theme = DefaultTheme() + return theme.Paragraph(text) +} + +func Divider() string { + var theme Theme = DefaultTheme() + return theme.Divider() +} + +// Applies the default margin to content +func Margin(content, margin string) string { + var theme Theme = DefaultTheme() + return theme.Margin(content, margin) +} + +// Applies the default padding to content +func Padding(content, margin string) string { + var theme Theme = DefaultTheme() + return theme.Padding(content, margin) +} + +func Container(content, width, height, background string) string { + var theme Theme = DefaultTheme() + return theme.Container(content, width, height, background) +} + +func Align(content, align string) string { + var theme Theme = DefaultTheme() + return theme.Align(content, align) +} diff --git a/examples/gno.land/p/demo/uif/uif_test.gno b/examples/gno.land/p/demo/uif/uif_test.gno new file mode 100644 index 00000000000..9665f1ea684 --- /dev/null +++ b/examples/gno.land/p/demo/uif/uif_test.gno @@ -0,0 +1 @@ +package uif diff --git a/examples/gno.land/r/demo/uif/gno.mod b/examples/gno.land/r/demo/uif/gno.mod new file mode 100644 index 00000000000..7ac9b0756fe --- /dev/null +++ b/examples/gno.land/r/demo/uif/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/demo/uif + +require ( + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/uif v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/uif/uif.gno b/examples/gno.land/r/demo/uif/uif.gno new file mode 100644 index 00000000000..a0768703d53 --- /dev/null +++ b/examples/gno.land/r/demo/uif/uif.gno @@ -0,0 +1,401 @@ +package uif + +import ( + "strings" + + "gno.land/p/demo/uif" +) + +// RenderHomePage - Renders the Home page with the introduction and navigation +func RenderHomePage() string { + // Define a custom theme for the page + customTheme := uif.Theme{ + PrimaryColor: "#3498db", // Blue color + SecondaryColor: "#2ecc71", // Green color + FontSize: "18px", // Slightly larger font size + BorderRadius: "8px", // More rounded corners + DefaultPadding: "12px 24px", // Custom padding for better spacing + DefaultMargin: "10px", + } + + // Navbar with links and href values using the theme method + navbarLinks := map[string]string{ + "Home": "/r/demo/uif", + "Components": "/r/demo/uif:components", + "Themes": "/r/demo/uif:themes", + "UI Package": "/r/demo/uif:ui-package", + } + navbar := customTheme.Navbar(navbarLinks) + + // Heading for the introduction + headingIntro := customTheme.Heading(1, "Welcome to the UIF Package Home Page") + + // Introduction paragraph + paragraphIntro := customTheme.Paragraph(` + The UIF package offers an easy way to create reusable UI components with a built-in theming system. + You can either use the default theme or define your own custom theme for a consistent and flexible UI design. + In this guide, you'll learn how to use the package, see examples, and discover how it can simplify UI development. + Whether you're creating forms, buttons, or entire layouts, UIF provides the tools you need to build + a clean, responsive interface. + `) + + // Feature Section: Benefits of Using UIF + headingFeatures := customTheme.Heading(2, "Why Use UIF?") + paragraphFeatures := customTheme.Paragraph(` + UIF provides several advantages for developers looking to streamline UI development: + + `) + + // How it Works Section + headingHowItWorks := customTheme.Heading(2, "How Does It Work?") + paragraphHowItWorks := customTheme.Paragraph(` + UIF makes it easy to build and customize UI components. With just a few lines of code, you can create buttons, + cards, text inputs, and other essential elements. Themes allow you to customize colors, fonts, padding, and more + to fit your application's branding or user preferences. + You can even create custom themes to meet specific design requirements. + `) + + // Call-to-action button for exploring the guide + buttonExplore := customTheme.Button("Explore Now", "/r/demo/uif:components") + + // Divider to separate sections + divider := customTheme.Divider() + + // Footer for the page + footer := customTheme.Footer([]string{"Privacy Policy", "Contact", "Terms of Service"}) + + // Organize content using grid layout + gridItems := []string{ + headingIntro + paragraphIntro + divider, + headingHowItWorks + paragraphHowItWorks + buttonExplore + divider, + headingFeatures + paragraphFeatures, + } + + // Create a grid with 1 column, 40px gap, and left-aligned text + contentGrid := customTheme.Grid(gridItems, 1, "0", "left") + + // Combine everything into the home page + return navbar + contentGrid + footer +} + +func RenderComponentsPage() string { + // Use default theme for the components page + + // Navbar with links and href values using default theme + navbarLinks := map[string]string{ + "Home": "/r/demo/uif", + "Components": "/r/demo/uif:components", + "Themes": "/r/demo/uif:themes", + "UI Package": "/r/demo/uif:ui-package", + } + navbar := uif.Navbar(navbarLinks) + + // Main title and introduction using default theme + headingMain := uif.Heading(1, "Components Overview") + descriptionMain := uif.Paragraph(` + This page provides an overview of the various UI components available in the UIF package. Each component is customizable + through the use of themes, allowing you to modify colors, padding, and other styling properties. Explore how these + components can be used to create beautiful, responsive user interfaces. + `) + + // Section: Button Component + buttonTitle := uif.Heading(2, "Button Component") + descriptionButton := uif.Paragraph(` + Buttons are interactive elements that users can click to trigger actions. You can define the button's text, + link, and appearance using the default theme. Adjust the padding, background colors, and border radius to fit your design. + `) + button := uif.Button("Click Me", "#") + // Define a custom theme for a large button + customLargeButtonTheme := uif.Theme{ + PrimaryColor: "#1abc9c", // Teal color + SecondaryColor: "#16a085", // Darker teal + DefaultPadding: "16px 32px", // Larger padding for a bigger button + FontSize: "20px", // Bigger font size + BorderRadius: "12px", + DefaultMargin: "10px", + } + buttonLarge := customLargeButtonTheme.Button("Learn More", "#") + buttonCode := uif.CodeBlock(` +button := uif.Button("Click Me", "#") +buttonLarge := customLargeButtonTheme.Button("Learn More", "#") + `) + + // Section: Text Input Component + textInputTitle := uif.Heading(2, "Text Input Component") + + descriptionTextInput := uif.Paragraph(` + TextInput fields allow users to input text. Customize the placeholder text, padding, and border radius using the default theme. + `) + + textInput := uif.TextInput("name", "Enter your name") + textInputEmail := uif.Theme{ + PrimaryColor: "#2980b9", // Blue color + DefaultPadding: "10px 20px", // Custom padding for the input + FontSize: "16px", // Regular font size + BorderRadius: "8px", + DefaultMargin: "10px", // Rounded corners + }.TextInput("email", "Enter your email") + textInputCode := uif.CodeBlock(` +textInput := uif.TextInput("name","Enter your name") +textInputEmail := uif.Theme{ + PrimaryColor: "#2980b9", + Padding: "10px 20px", + FontSize: "16px", + BorderRadius: "8px", +}.TextInput("email","Enter your email") + `) + + formTitle := uif.Heading(2, "Form Component") + descriptionForm := uif.Paragraph(` + Forms allow users to input data, which can then be submitted to the help page of realm. + `) + + // Form fields using TextInput + fields := []string{ + uif.TextInput("addr", "Enter address"), + } + + // Create the form with action URL + form := uif.Form(fields, "/r/demo/users", "GetUserByAddress", "Submit") + + // Section: Card Component with custom width and height + cardTitle := uif.Heading(2, "Card Component") + descriptionCard := uif.Paragraph(` + The Card component is used to group related content inside a bordered, padded container. + It’s commonly used to display content in a neatly organized box with a consistent style. + You can use the theme to modify the card's padding, border, and shadow effects, as well as its width and height. + `) + cardContent := uif.Paragraph("This is a card component with some content.") + card := uif.Card(cardContent, "", "") // Card with default dimensions + cardWithImage := uif.Card(uif.Image("https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg", "", "200px")+uif.Paragraph("This card contains an image and text."), "400px", "auto") + cardCode := uif.CodeBlock(` +cardContent := uif.Paragraph("This is a card component with some content.") +card := uif.Card(cardContent, "", "") // Card with default dimensions +// Card with image and custom dimensions +cardWithImage := uif.Card(uif.Image("https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg", "300px", "200px") + uif.Paragraph("This card contains an image and text."), "400px", "auto") + `) + + // Section: Divider Component + dividerTitle := uif.Heading(2, "Divider Component") + descriptionDivider := uif.Paragraph(` + Dividers are horizontal lines used to visually separate sections of content. They can be styled using the default theme + to change their color, thickness, and margins. + `) + divider := uif.Divider() + dividerCode := uif.CodeBlock(` +divider := uif.Divider() + `) + + // Section: Image Component with custom dimensions + imageTitle := uif.Heading(2, "Image Component") + descriptionImage := uif.Paragraph(` + The Image component allows you to add pictures to your page. You can adjust the border radius, width, and height + to fit your design. Images can be displayed in cards or standalone. + `) + image := uif.Image("https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg", "300px", "200px") + imageCode := uif.CodeBlock(` +image := uif.Image("https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg", "300px", "200px") + `) + + // Footer + footer := uif.Footer([]string{"Privacy Policy", "Contact", "Terms of Service"}) + + // Combine everything into the components page + return navbar + headingMain + descriptionMain + + imageTitle + descriptionImage + image + imageCode + + cardTitle + descriptionCard + card + cardWithImage + cardCode + + buttonTitle + descriptionButton + button + buttonLarge + buttonCode + + textInputTitle + descriptionTextInput + textInput + textInputEmail + textInputCode + + formTitle + descriptionForm + form + + dividerTitle + descriptionDivider + divider + dividerCode + + footer +} + +// RenderThemesPage - Renders the page that explains how to apply themes in UIF +func RenderThemesPage(themeColor string) string { + // Define available themes in a map + themes := map[string]uif.Theme{ + "purple": { + PrimaryColor: "#8e44ad", // Purple color + SecondaryColor: "#9b59b6", // Light purple + FontSize: "18px", // Larger font size + DefaultPadding: "12px 24px", // Custom padding + BorderRadius: "10px", + DefaultMargin: "10px", // Rounded corners + }, + "blue": { + PrimaryColor: "#2980b9", // Blue color + SecondaryColor: "#3498db", // Light blue + FontSize: "16px", // Smaller font size + DefaultPadding: "10px 20px", // Custom padding + BorderRadius: "8px", + DefaultMargin: "10px", // Slightly rounded corners + }, + "green": { + PrimaryColor: "#27ae60", // Green color + SecondaryColor: "#2ecc71", // Light green + FontSize: "20px", // Larger font size + DefaultPadding: "14px 28px", // Larger padding + BorderRadius: "12px", + DefaultMargin: "10px", // More rounded corners + }, + } + + // Select the theme based on the themeColor argument + theme, exists := themes[themeColor] + if !exists { + // Default to purple if the theme is not recognized + theme = themes["purple"] + } + + // Navbar with links and href values + navbarLinks := map[string]string{ + "Home": "/r/demo/uif", + "Components": "/r/demo/uif:components", + "Themes": "/r/demo/uif:themes", + "UI Package": "/r/demo/uif:ui-package", + } + navbar := theme.Navbar(navbarLinks) + + // Main themes overview heading and description + heading := theme.Heading(1, "Themes Overview") + paragraph := theme.Paragraph(` + The UIF package allows you to define and apply themes for consistency across your UI components. Themes help you maintain + a cohesive design by allowing you to set properties such as primary and secondary colors, font size, border radius, + and padding, which are applied to all UI elements. + `) + + // Section: Benefits of Using Themes + benefitsTitle := theme.Heading(2, "Why Use Themes?") + benefitsText := theme.Paragraph(` + Using themes ensures a consistent look and feel across your entire application. By defining colors, font sizes, + and other styles in one place, you make it easier to update your design. It also allows you to quickly switch between + different styles based on user preferences or branding requirements. + `) + + // Section: How to Define a Theme + defineTitle := theme.Heading(2, "How to Define a Theme") + defineText := theme.Paragraph(` + A theme in UIF is defined by specifying a primary color, secondary color, font size, padding, and border radius. These + properties are applied to all UI components such as buttons, cards, text inputs, and more. Here's an example of how to + define and apply a theme in UIF: + `) + defineCode := theme.CodeBlock(` +theme := uif.Theme{ + PrimaryColor: "#8e44ad", // Purple + SecondaryColor: "#9b59b6", // Light Purple + FontSize: "18px", // Larger font + DefaultPadding: "12px 24px", // Custom padding + BorderRadius: "10px", // Rounded corners +} +button := theme.Button("Click Me", "#") + `) + + // Dynamic button creation for each theme + themeButtonsTitle := theme.Heading(2, "Apply a Theme") + themeButtonsText := theme.Paragraph(` + Click on the buttons below to see how different themes can be applied across your UI components. Each button represents + a unique theme with different color schemes and styles. + `) + buttonPurple := themes["purple"].Button("Apply Purple Theme", "/r/demo/uif:themes/purple") + buttonBlue := themes["blue"].Button("Apply Blue Theme", "/r/demo/uif:themes/blue") + buttonGreen := themes["green"].Button("Apply Green Theme", "/r/demo/uif:themes/green") + + // Section: Example UI Components with Selected Theme + exampleTitle := theme.Heading(2, "Example Components with the Current Theme") + exampleText := theme.Paragraph(` + Below are examples of how the selected theme is applied to various UI components. You can see how the colors, padding, + and other styles change based on the current theme. + `) + buttonExample := theme.Button("Sample Button", "#") + cardExample := theme.Card(theme.Paragraph("This is a sample card with the current theme."), "300px", "auto") + inputExample := theme.TextInput("name", "Enter your name") + + // Divider + divider := theme.Divider() + + // Footer + footer := theme.Footer([]string{"Privacy Policy", "Contact", "Terms of Service"}) + + // Combine everything into the themes page + return navbar + + heading + + paragraph + + benefitsTitle + + benefitsText + + defineTitle + + defineText + + defineCode + + themeButtonsTitle + + themeButtonsText + + buttonPurple + buttonBlue + buttonGreen + + exampleTitle + + exampleText + + buttonExample + cardExample + inputExample + + divider + footer +} + +// RenderUIPackagePage - Renders the UI Package page +func RenderUIPackagePage() string { + // Define a theme for the UI Package page + theme := uif.Theme{ + PrimaryColor: "#2980b9", // Dark blue color + SecondaryColor: "#3498db", // Light blue color + FontSize: "18px", // Larger font size + DefaultPadding: "12px 24px", // Custom padding + BorderRadius: "10px", + DefaultMargin: "10px", // Rounded corners + } + + // Navbar with links and href values + navbarLinks := map[string]string{ + "Home": "/r/demo/uif", + "Components": "/r/demo/uif:components", + "Themes": "/r/demo/uif:themes", + "UI Package": "/r/demo/uif:ui-package", + } + navbar := theme.Navbar(navbarLinks) + + // UI Package page content + heading := theme.Heading(1, "UI Package Overview") + paragraph := theme.Paragraph(`The UIF package includes everything you need to build beautiful and responsive user interfaces.`) + + // Button + button := theme.Button("Get Started with UIF", "#") + + // Divider + divider := theme.Divider() + + // Footer + footer := theme.Footer([]string{"Privacy Policy", "Contact", "Terms of Service"}) + + // Combine everything into the UI package page + return navbar + heading + paragraph + button + divider + footer +} + +// Render - the main entry point for displaying different pages +func Render(path string) string { + segments := strings.Split(path, "/") + + if len(segments) > 1 && segments[0] == "themes" { + themeColor := segments[1] + return RenderThemesPage(themeColor) + } + + switch segments[0] { + case "components": + return RenderComponentsPage() + case "themes": + return RenderThemesPage("") + case "ui-package": + return RenderUIPackagePage() + default: + // Default to the home page + return RenderHomePage() + } +} diff --git a/examples/gno.land/r/demo/uif/uif_test.gno b/examples/gno.land/r/demo/uif/uif_test.gno new file mode 100644 index 00000000000..5a46cf2dbce --- /dev/null +++ b/examples/gno.land/r/demo/uif/uif_test.gno @@ -0,0 +1,13 @@ +package uif + +import ( + "testing" + + "gno.land/p/demo/uassert" +) + +func TestRender(t *testing.T) { + got := Render("") + expected := "# UI Demo\n\n[foo](r/demo/ui:foo) / [bar](r/demo/ui:foo/bar)\n\n\nSimple UI demonstration.\n\n- a text\n- [a relative link](r/demo/ui:foobar)\n- another text\n- **a bold text**\n- _italic text_\n- raw markdown with **bold** text in the middle.\n- `some inline code`\n- [a remote link](https://gno.land)\n\nanother string.\n\na paragraph.\n\n\n---\n\n\nI'm the footer.\n\n" + uassert.Equal(t, expected, got) +}