diff --git a/packages/dashboard/seed/metadata.json b/packages/dashboard/seed/metadata.json index 0e1121b38..70e3ef091 100644 --- a/packages/dashboard/seed/metadata.json +++ b/packages/dashboard/seed/metadata.json @@ -2,11 +2,12 @@ "packageId": "dashboard", "name": "Dashboard", "version": "1.0.0", - "description": "Dashboard components", + "description": "Dashboard layouts, stat cards, and widgets", "author": "MetaBuilder", "category": "ui", "dependencies": [], "exports": { - "components": [] + "components": ["StatCard", "DashboardGrid", "Widget"], + "scripts": ["stats", "layout"] } } \ No newline at end of file diff --git a/packages/form_builder/seed/metadata.json b/packages/form_builder/seed/metadata.json index ee860a4c5..73c52c214 100644 --- a/packages/form_builder/seed/metadata.json +++ b/packages/form_builder/seed/metadata.json @@ -2,11 +2,12 @@ "packageId": "form_builder", "name": "Form Builder", "version": "1.0.0", - "description": "Form builder components", + "description": "Form fields, validation, and submission handling", "author": "MetaBuilder", "category": "ui", "dependencies": [], "exports": { - "components": [] + "components": ["FormField", "EmailField", "PasswordField", "NumberField", "SearchBar"], + "scripts": ["fields", "validate"] } } \ No newline at end of file diff --git a/packages/form_builder/seed/scripts/fields.lua b/packages/form_builder/seed/scripts/fields.lua new file mode 100644 index 000000000..988bff6df --- /dev/null +++ b/packages/form_builder/seed/scripts/fields.lua @@ -0,0 +1,53 @@ +local M = {} + +function M.text(props) + return { + type = "Box", + children = { + props.label and { type = "Label", props = { text = props.label, htmlFor = props.name } } or nil, + { type = "Input", props = { name = props.name, placeholder = props.placeholder, required = props.required } } + } + } +end + +function M.email(props) + return { + type = "Box", + children = { + { type = "Label", props = { text = props.label or "Email", htmlFor = props.name } }, + { type = "Input", props = { name = props.name, type = "email", placeholder = "you@example.com" } } + } + } +end + +function M.password(props) + return { + type = "Box", + children = { + { type = "Label", props = { text = props.label or "Password", htmlFor = props.name } }, + { type = "Input", props = { name = props.name, type = "password", placeholder = "••••••••" } } + } + } +end + +function M.number(props) + return { + type = "Box", + children = { + props.label and { type = "Label", props = { text = props.label, htmlFor = props.name } } or nil, + { type = "Input", props = { name = props.name, type = "number", min = props.min, max = props.max } } + } + } +end + +function M.textarea(props) + return { + type = "Box", + children = { + props.label and { type = "Label", props = { text = props.label, htmlFor = props.name } } or nil, + { type = "TextArea", props = { name = props.name, rows = props.rows or 4, placeholder = props.placeholder } } + } + } +end + +return M diff --git a/packages/form_builder/seed/scripts/init.lua b/packages/form_builder/seed/scripts/init.lua new file mode 100644 index 000000000..129974c10 --- /dev/null +++ b/packages/form_builder/seed/scripts/init.lua @@ -0,0 +1,3 @@ +local M = {} +function M.on_install(ctx) return { message = "Form Builder installed", version = ctx.version } end +return M diff --git a/packages/form_builder/seed/scripts/manifest.json b/packages/form_builder/seed/scripts/manifest.json new file mode 100644 index 000000000..b26d7e346 --- /dev/null +++ b/packages/form_builder/seed/scripts/manifest.json @@ -0,0 +1,7 @@ +{ + "scripts": [ + { "file": "init.lua", "name": "init", "category": "lifecycle", "description": "Package lifecycle" }, + { "file": "fields.lua", "name": "fields", "category": "ui", "description": "Form field renderers" }, + { "file": "validate.lua", "name": "validate", "category": "validation", "description": "Field validation" } + ] +} diff --git a/packages/form_builder/seed/scripts/validate.lua b/packages/form_builder/seed/scripts/validate.lua new file mode 100644 index 000000000..8b210ca56 --- /dev/null +++ b/packages/form_builder/seed/scripts/validate.lua @@ -0,0 +1,47 @@ +local M = {} + +function M.required(value) + return value ~= nil and value ~= "" +end + +function M.email(value) + if not value then return false end + return string.match(value, "^[^@]+@[^@]+%.[^@]+$") ~= nil +end + +function M.minLength(value, min) + return value and #value >= min +end + +function M.maxLength(value, max) + return not value or #value <= max +end + +function M.pattern(value, pat) + return value and string.match(value, pat) ~= nil +end + +function M.number(value) + return tonumber(value) ~= nil +end + +function M.range(value, min, max) + local n = tonumber(value) + return n and n >= min and n <= max +end + +function M.validate_field(value, rules) + local errors = {} + for _, rule in ipairs(rules) do + if rule.type == "required" and not M.required(value) then + errors[#errors + 1] = rule.message or "Required" + elseif rule.type == "email" and not M.email(value) then + errors[#errors + 1] = rule.message or "Invalid email" + elseif rule.type == "minLength" and not M.minLength(value, rule.min) then + errors[#errors + 1] = rule.message or ("Min " .. rule.min .. " chars") + end + end + return { valid = #errors == 0, errors = errors } +end + +return M diff --git a/packages/nav_menu/seed/metadata.json b/packages/nav_menu/seed/metadata.json index a99600e4f..8b5370884 100644 --- a/packages/nav_menu/seed/metadata.json +++ b/packages/nav_menu/seed/metadata.json @@ -2,11 +2,12 @@ "packageId": "nav_menu", "name": "Navigation Menu", "version": "1.0.0", - "description": "Navigation menu components", + "description": "Sidebar, navigation menus, and breadcrumbs", "author": "MetaBuilder", "category": "ui", - "dependencies": [], + "dependencies": ["ui_permissions"], "exports": { - "components": [] + "components": ["Sidebar", "NavigationMenu", "Breadcrumbs"], + "scripts": ["sidebar", "menu"] } } \ No newline at end of file diff --git a/packages/nav_menu/seed/scripts/init.lua b/packages/nav_menu/seed/scripts/init.lua new file mode 100644 index 000000000..7a9bfe874 --- /dev/null +++ b/packages/nav_menu/seed/scripts/init.lua @@ -0,0 +1,3 @@ +local M = {} +function M.on_install(ctx) return { message = "Nav Menu installed", version = ctx.version } end +return M diff --git a/packages/nav_menu/seed/scripts/manifest.json b/packages/nav_menu/seed/scripts/manifest.json new file mode 100644 index 000000000..84e5cdc15 --- /dev/null +++ b/packages/nav_menu/seed/scripts/manifest.json @@ -0,0 +1,7 @@ +{ + "scripts": [ + { "file": "init.lua", "name": "init", "category": "lifecycle", "description": "Package lifecycle" }, + { "file": "sidebar.lua", "name": "sidebar", "category": "ui", "description": "Sidebar rendering" }, + { "file": "menu.lua", "name": "menu", "category": "ui", "description": "Navigation menu" } + ] +} diff --git a/packages/nav_menu/seed/scripts/menu.lua b/packages/nav_menu/seed/scripts/menu.lua new file mode 100644 index 000000000..2e46a8f1e --- /dev/null +++ b/packages/nav_menu/seed/scripts/menu.lua @@ -0,0 +1,44 @@ +local check = require("check") +local M = {} + +function M.render(props) + local items = {} + for _, item in ipairs(props.items or {}) do + if M.can_show(props.user, item) then + items[#items + 1] = M.item(item) + end + end + return { + type = "Flex", + props = { className = "items-center gap-4" }, + children = items + } +end + +function M.can_show(user, item) + if not item.minLevel then return true end + return check.can_access(user, item.minLevel) +end + +function M.item(item) + if item.children then + return { + type = "DropdownMenu", + children = { + { type = "DropdownMenuTrigger", props = { text = item.label } }, + { type = "DropdownMenuContent", children = M.sub_items(item.children) } + } + } + end + return { type = "Button", props = { variant = "ghost", text = item.label, onClick = "navigate", data = item.path } } +end + +function M.sub_items(children) + local items = {} + for _, c in ipairs(children) do + items[#items + 1] = { type = "DropdownMenuItem", props = { text = c.label, onClick = "navigate", data = c.path } } + end + return items +end + +return M diff --git a/packages/nav_menu/seed/scripts/sidebar.lua b/packages/nav_menu/seed/scripts/sidebar.lua new file mode 100644 index 000000000..62e5fa01f --- /dev/null +++ b/packages/nav_menu/seed/scripts/sidebar.lua @@ -0,0 +1,42 @@ +local M = {} + +function M.render(props) + local items = {} + for _, item in ipairs(props.items or {}) do + items[#items + 1] = M.item(item, props.currentPath) + end + return { + type = "Box", + props = { className = "w-64 border-r h-screen bg-sidebar" }, + children = { + M.header(props), + { type = "Stack", props = { spacing = 1, className = "p-4" }, children = items } + } + } +end + +function M.header(props) + return { + type = "Box", + props = { className = "p-4 border-b" }, + children = { + { type = "Typography", props = { variant = "h6", text = props.title or "Menu" } } + } + } +end + +function M.item(item, currentPath) + local active = currentPath == item.path + return { + type = "Button", + props = { + variant = active and "secondary" or "ghost", + className = "w-full justify-start", + text = item.label, + onClick = "navigate", + data = item.path + } + } +end + +return M