Skip to content

Commit 548145d

Browse files
committed
UI: Massive expansion of UI primitives and standard library (UI Kit)
1 parent fcfa362 commit 548145d

5 files changed

Lines changed: 226 additions & 14 deletions

File tree

include/scanner.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,11 @@ typedef enum {
170170
TOKEN_UI_WINDOW,
171171
TOKEN_UI_CONTAINER,
172172
TOKEN_UI_BUTTON,
173-
TOKEN_UI_TEXT,
174-
TOKEN_UI_INPUT,
175-
TOKEN_UI_STATE,
176-
TOKEN_UI_ACTION,
177-
TOKEN_UI_STYLE,
173+
TOKEN_UI_TEXT, TOKEN_UI_INPUT,
174+
TOKEN_UI_IMAGE, TOKEN_UI_LINK, TOKEN_UI_LIST, TOKEN_UI_ITEM,
175+
TOKEN_UI_SECTION, TOKEN_UI_NAV, TOKEN_UI_HEADER, TOKEN_UI_FOOTER,
176+
TOKEN_UI_ICON, TOKEN_UI_FORM, TOKEN_UI_LABEL, TOKEN_UI_SELECT, TOKEN_UI_OPTION,
177+
TOKEN_UI_STATE, TOKEN_UI_ACTION, TOKEN_UI_STYLE,
178178

179179
TOKEN_ERROR,
180180
TOKEN_EOF

src/compiler/lexer/scanner.c

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,31 @@ static PxTokenType identifierType(Scanner *scanner) {
186186
}
187187
}
188188
break;
189+
case 'F':
190+
if (scanner->current - scanner->start > 1) {
191+
switch (scanner->start[1]) {
192+
case 'o':
193+
if (scanner->current - scanner->start > 2) {
194+
switch (scanner->start[2]) {
195+
case 'o': return checkKeyword(scanner, 3, 3, "ter", TOKEN_UI_FOOTER);
196+
case 'r': return checkKeyword(scanner, 3, 1, "m", TOKEN_UI_FORM);
197+
}
198+
}
199+
break;
200+
}
201+
}
202+
break;
203+
case 'H': return checkKeyword(scanner, 1, 5, "eader", TOKEN_UI_HEADER);
204+
case 'I':
205+
if (scanner->current - scanner->start > 1) {
206+
switch (scanner->start[1]) {
207+
case 'm': return checkKeyword(scanner, 2, 3, "age", TOKEN_UI_IMAGE);
208+
case 'c': return checkKeyword(scanner, 2, 2, "on", TOKEN_UI_ICON);
209+
case 't': return checkKeyword(scanner, 2, 2, "em", TOKEN_UI_ITEM);
210+
case 'n': return checkKeyword(scanner, 2, 3, "put", TOKEN_UI_INPUT);
211+
}
212+
}
213+
break;
189214
case 'b': return checkKeyword(scanner, 1, 4, "reak", TOKEN_BREAK);
190215
case 'B': return checkKeyword(scanner, 1, 5, "utton", TOKEN_UI_BUTTON);
191216
case 'c':
@@ -338,7 +363,21 @@ static PxTokenType identifierType(Scanner *scanner) {
338363
}
339364
}
340365
break;
341-
case 'I': return checkKeyword(scanner, 1, 4, "nput", TOKEN_UI_INPUT);
366+
case 'L':
367+
if (scanner->current - scanner->start > 1) {
368+
switch (scanner->start[1]) {
369+
case 'i':
370+
if (scanner->current - scanner->start > 2) {
371+
switch (scanner->start[2]) {
372+
case 'n': return checkKeyword(scanner, 3, 1, "k", TOKEN_UI_LINK);
373+
case 's': return checkKeyword(scanner, 3, 1, "t", TOKEN_UI_LIST);
374+
}
375+
}
376+
break;
377+
case 'a': return checkKeyword(scanner, 2, 3, "bel", TOKEN_UI_LABEL);
378+
}
379+
}
380+
break;
342381
case 'k':
343382
if (scanner->current - scanner->start > 1) {
344383
switch(scanner->start[1]) {
@@ -378,11 +417,12 @@ static PxTokenType identifierType(Scanner *scanner) {
378417
switch (scanner->start[1]) {
379418
case 'a': return checkKeyword(scanner, 2, 4, "tive", TOKEN_NATIVE);
380419
case 'e': return checkKeyword(scanner, 2, 1, "w", TOKEN_NEW);
381-
case 'u': return checkKeyword(scanner, 2, 2, "ll", TOKEN_NULL);
382420
case 'o': return checkKeyword(scanner, 2, 2, "de", TOKEN_NODE);
383421
}
384422
}
385423
break;
424+
case 'N': return checkKeyword(scanner, 1, 2, "av", TOKEN_UI_NAV);
425+
case 'O': return checkKeyword(scanner, 1, 5, "ption", TOKEN_UI_OPTION);
386426
case 'p':
387427
if (scanner->current - scanner->start > 1) {
388428
switch (scanner->start[1]) {
@@ -464,9 +504,10 @@ static PxTokenType identifierType(Scanner *scanner) {
464504
break;
465505
case 'S':
466506
if (scanner->current - scanner->start > 1) {
467-
switch (scanner->start[1]) {
468507
case 't': return checkKeyword(scanner, 2, 3, "ate", TOKEN_UI_STATE);
469508
case 'T': return checkKeyword(scanner, 2, 3, "YLE", TOKEN_UI_STYLE);
509+
case 'e': return checkKeyword(scanner, 2, 5, "ction", TOKEN_UI_SECTION);
510+
case 'l': return checkKeyword(scanner, 2, 4, "ect", TOKEN_UI_SELECT);
470511
}
471512
}
472513
break;

src/compiler/parser/parser.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,10 @@ static Stmt *statement(Parser *p) {
750750
if (match(p, 1, TOKEN_UI_ACTION)) return uiActionDecl(p);
751751

752752
// Generic UI Component (fallback for other UI tokens)
753-
if (match(p, 5, TOKEN_UI_CONTAINER, TOKEN_UI_BUTTON, TOKEN_UI_TEXT, TOKEN_UI_INPUT, TOKEN_UI_STYLE)) {
753+
if (match(p, 18, TOKEN_UI_CONTAINER, TOKEN_UI_BUTTON, TOKEN_UI_TEXT, TOKEN_UI_INPUT, TOKEN_UI_STYLE,
754+
TOKEN_UI_IMAGE, TOKEN_UI_LINK, TOKEN_UI_LIST, TOKEN_UI_ITEM, TOKEN_UI_SECTION,
755+
TOKEN_UI_NAV, TOKEN_UI_HEADER, TOKEN_UI_FOOTER, TOKEN_UI_ICON, TOKEN_UI_FORM,
756+
TOKEN_UI_LABEL, TOKEN_UI_SELECT, TOKEN_UI_OPTION)) {
754757
return uiComponentDecl(p);
755758
}
756759

src/compiler/transpiler_ui.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ void transpileUIApp(Stmt *appStmt, const char *outputDir) {
6060
fclose(js);
6161
}
6262

63+
static const char *mapTag(const char *tag) {
64+
if (strcmp(tag, "Container") == 0) return "div";
65+
if (strcmp(tag, "Button") == 0) return "button";
66+
if (strcmp(tag, "Text") == 0) return "span";
67+
if (strcmp(tag, "Input") == 0) return "input";
68+
if (strcmp(tag, "Image") == 0) return "img";
69+
if (strcmp(tag, "Link") == 0) return "a";
70+
if (strcmp(tag, "List") == 0) return "ul";
71+
if (strcmp(tag, "Item") == 0) return "li";
72+
if (strcmp(tag, "Section") == 0) return "section";
73+
if (strcmp(tag, "Nav") == 0) return "nav";
74+
if (strcmp(tag, "Header") == 0) return "header";
75+
if (strcmp(tag, "Footer") == 0) return "footer";
76+
if (strcmp(tag, "Icon") == 0) return "i";
77+
if (strcmp(tag, "Form") == 0) return "form";
78+
if (strcmp(tag, "Label") == 0) return "label";
79+
if (strcmp(tag, "Select") == 0) return "select";
80+
if (strcmp(tag, "Option") == 0) return "option";
81+
return tag;
82+
}
83+
6384
static void transpileStmt(Stmt *stmt, FILE *html, FILE *js, int indent) {
6485
if (!stmt) return;
6586

@@ -86,8 +107,9 @@ static void transpileStmt(Stmt *stmt, FILE *html, FILE *js, int indent) {
86107
break;
87108

88109
case STMT_UI_COMPONENT: {
110+
const char *htmlTag = mapTag(stmt->as.ui_component.tag);
89111
printIndent(html, indent);
90-
fprintf(html, "<%s", stmt->as.ui_component.tag);
112+
fprintf(html, "<%s", htmlTag);
91113

92114
// Props
93115
DictPairList *props = stmt->as.ui_component.props;
@@ -98,6 +120,12 @@ static void transpileStmt(Stmt *stmt, FILE *html, FILE *js, int indent) {
98120
transpileExpr(props->items[i].value, html);
99121
fprintf(html, "\"");
100122
}
123+
124+
if (strcmp(htmlTag, "img") == 0 || strcmp(htmlTag, "input") == 0) {
125+
fprintf(html, " />\n");
126+
break;
127+
}
128+
101129
fprintf(html, ">");
102130

103131
// Children
@@ -108,7 +136,7 @@ static void transpileStmt(Stmt *stmt, FILE *html, FILE *js, int indent) {
108136
}
109137
printIndent(html, indent);
110138
}
111-
fprintf(html, "</%s>\n", stmt->as.ui_component.tag);
139+
fprintf(html, "</%s>\n", htmlTag);
112140
break;
113141
}
114142

std/lib/ui.prox

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,148 @@
22
// Project: ProX Programming Language (ProXPL)
33
// Author: ProgrammerKR
44
// Created: 2026-02-26
5-
// Description: UI Library marker file.
5+
// Description: ProXPL UI Standard Library (UI Kit)
66
// --------------------------------------------------
77

8-
// This file serves as a marker for the built-in UI library.
9-
// Importing this library enables UI keywords and components.
8+
// --- Design System Tokens ---
9+
10+
// Colors (ProX Design System)
11+
const COLOR_PRIMARY = "#3b82f6";
12+
const COLOR_SECONDARY = "#10b981";
13+
const COLOR_DANGER = "#ef4444";
14+
const COLOR_WARNING = "#f59e0b";
15+
const COLOR_INFO = "#3b82f6";
16+
const COLOR_DARK = "#1f2937";
17+
const COLOR_LIGHT = "#f3f4f6";
18+
const COLOR_WHITE = "#ffffff";
19+
const COLOR_BLACK = "#000000";
20+
21+
// Gradients
22+
const GRADIENT_PRIMARY = "linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)";
23+
const GRADIENT_DARK = "linear-gradient(135deg, #1f2937 0%, #111827 100%)";
24+
const GRADIENT_SUCCESS = "linear-gradient(135deg, #10b981 0%, #059669 100%)";
25+
26+
// Typography
27+
const FONT_SANS = "Inter, system-ui, sans-serif";
28+
const FONT_MONO = "Fira Code, monospace";
29+
const TEXT_XS = "0.75rem";
30+
const TEXT_SM = "0.875rem";
31+
const TEXT_BASE = "1rem";
32+
const TEXT_LG = "1.125rem";
33+
const TEXT_XL = "1.25rem";
34+
const TEXT_2XL = "1.5rem";
35+
const TEXT_3XL = "1.875rem";
36+
37+
// Spacing (Utility Classes)
38+
const P_0 = "padding: 0";
39+
const P_1 = "padding: 0.25rem";
40+
const P_2 = "padding: 0.5rem";
41+
const P_4 = "padding: 1rem";
42+
const P_8 = "padding: 2rem";
43+
44+
const M_0 = "margin: 0";
45+
const M_1 = "margin: 0.25rem";
46+
const M_2 = "margin: 0.5rem";
47+
const M_4 = "margin: 1rem";
48+
const M_auto = "margin: auto";
49+
50+
// --- Styled Component Primitives (CSS Class Strings) ---
51+
52+
// Layouts
53+
const LAYOUT_CENTER = "display: flex; justify-content: center; align-items: center;";
54+
const LAYOUT_COL = "display: flex; flex-direction: column;";
55+
const LAYOUT_ROW = "display: flex; flex-direction: row;";
56+
const LAYOUT_GRID = "display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;";
57+
58+
// Buttons
59+
const BTN_BASE = "px-4 py-2 rounded-lg font-medium transition-all transform hover:scale-105 active:scale-95 cursor-pointer";
60+
const BTN_PRIMARY = "bg-blue-600 text-white hover:bg-blue-700 shadow-lg";
61+
const BTN_SECONDARY = "bg-green-600 text-white hover:bg-green-700 shadow-lg";
62+
const BTN_OUTLINE = "border-2 border-blue-600 text-blue-600 hover:bg-blue-50";
63+
const BTN_GHOST = "text-gray-600 hover:bg-gray-100";
64+
65+
// Inputs
66+
const INPUT_BASE = "w-full px-4 py-2 border-2 border-gray-200 rounded-lg focus:border-blue-500 transition-all outline-none";
67+
const INPUT_ERROR = "border-red-500 focus:border-red-600";
68+
const INPUT_SUCCESS = "border-green-500 focus:border-green-600";
69+
70+
// Cards
71+
const CARD_BASE = "bg-white p-6 rounded-2xl shadow-xl border border-gray-100 hover:shadow-2xl transition-shadow";
72+
const CARD_GLASS = "backdrop-blur-xl bg-white/30 border border-white/20 shadow-2xl rounded-2xl p-6";
73+
74+
// Navigation
75+
const NAV_BASE = "flex items-center justify-between px-8 py-4 bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50";
76+
const NAV_LINK = "text-gray-600 hover:text-blue-600 font-medium transition-colors";
77+
78+
// --- Animation Keyframes (CSS Snippets) ---
79+
80+
const ANIM_FADE_IN = "@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } animation: fadeIn 0.5s ease-out;";
81+
const ANIM_SLIDE_UP = "@keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } animation: slideUp 0.5s ease-out;";
82+
const ANIM_BOUNCE = "@keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } animation: bounce 1s infinite;";
83+
const ANIM_SPIN = "@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } animation: spin 1s linear infinite;";
84+
85+
// --- Shadow Tiers ---
86+
const SHADOW_SM = "box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);";
87+
const SHADOW_MD = "box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);";
88+
const SHADOW_LG = "box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);";
89+
const SHADOW_XL = "box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);";
90+
const SHADOW_INNER = "box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);";
91+
92+
// --- Responsive Breakpoints (Helper Strings) ---
93+
const SCREEN_SM = "640px";
94+
const SCREEN_MD = "768px";
95+
const SCREEN_LG = "1024px";
96+
const SCREEN_XL = "1280px";
97+
98+
// --- Layout Presets ---
99+
const CENTER_FULL = "display: flex; justify-content: center; align-items: center; min-height: 100vh; background: " + COLOR_LIGHT;
100+
const GRID_DASHBOARD = "display: grid; grid-template-columns: 250px 1fr; min-height: 100vh;";
101+
const FLEX_BETWEEN = "display: flex; justify-content: space-between; align-items: center;";
102+
103+
// --- Global Theme Object ---
104+
const THEME_DARK = "background-color: " + COLOR_DARK + "; color: " + COLOR_WHITE + ";";
105+
const THEME_LIGHT = "background-color: " + COLOR_WHITE + "; color: " + COLOR_BLACK + ";";
106+
107+
// --- More CSS Utilities ---
108+
const CURSOR_POINTER = "cursor: pointer;";
109+
const SELECT_NONE = "user-select: none;";
110+
const OVERFLOW_HIDDEN = "overflow: hidden;";
111+
const BORDER_B = "border-bottom: 1px solid " + COLOR_LIGHT;
112+
const TRANSITION_FAST = "transition: all 0.2s ease-in-out;";
113+
const TRANSITION_NORMAL = "transition: all 0.3s ease-in-out;";
114+
115+
// --- Standard UI Components (Templates) ---
116+
117+
// Note: These can be used in className props
118+
const UTIL_HIDDEN = "display: none;";
119+
const UTIL_FLEX = "display: flex;";
120+
const UTIL_W_FULL = "width: 100%;";
121+
const UTIL_H_FULL = "height: 100%;";
122+
const UTIL_ROUNDED_FULL = "border-radius: 9999px;";
123+
124+
// --- Standard UI Components (Templates) ---
125+
126+
/*
127+
Usage Example:
128+
use UI;
129+
130+
App Main {
131+
State userLoggedIn = false;
132+
133+
Container(style: CARD_BASE) {
134+
Header() {
135+
Icon(className: "fas fa-user", style: "color: " + COLOR_PRIMARY);
136+
Text(style: "font-weight: bold; margin-left: 0.5rem") { "Account Details" }
137+
}
138+
139+
Section(style: "margin-top: 1rem") {
140+
Input(placeholder: "Username", style: INPUT_BASE);
141+
Button(style: BTN_BASE + " " + BTN_PRIMARY) { "Login" }
142+
}
143+
144+
Footer() {
145+
Link(href: "#", style: NAV_LINK) { "Forgot Password?" }
146+
}
147+
}
148+
}
149+
*/

0 commit comments

Comments
 (0)