@maccesar/titools 2.2.12 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -713
- package/bin/titools.js +1 -1
- package/lib/commands/update.js +5 -6
- package/lib/config.js +1 -0
- package/lib/downloader.js +10 -0
- package/package.json +1 -1
- package/skills/alloy-guides/SKILL.md +46 -48
- package/skills/alloy-guides/references/CONTROLLERS.md +4 -6
- package/skills/alloy-guides/references/MODELS.md +340 -184
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +3 -4
- package/skills/alloy-guides/references/VIEWS_STYLES.md +44 -46
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +4 -5
- package/skills/alloy-guides/references/VIEWS_XML.md +51 -17
- package/skills/alloy-guides/references/WIDGETS.md +1 -1
- package/skills/alloy-howtos/SKILL.md +12 -13
- package/skills/alloy-howtos/references/cli_reference.md +40 -41
- package/skills/alloy-howtos/references/samples.md +3 -4
- package/skills/purgetss/SKILL.md +369 -356
- package/skills/purgetss/references/EXAMPLES.md +24 -25
- package/skills/purgetss/references/animation-advanced.md +555 -0
- package/skills/purgetss/references/animation-system.md +395 -995
- package/skills/purgetss/references/apply-directive.md +111 -141
- package/skills/purgetss/references/arbitrary-values.md +206 -480
- package/skills/purgetss/references/class-categories.md +277 -0
- package/skills/purgetss/references/class-index.md +1 -420
- package/skills/purgetss/references/cli-commands.md +446 -556
- package/skills/purgetss/references/configurable-properties.md +163 -599
- package/skills/purgetss/references/custom-rules.md +33 -76
- package/skills/purgetss/references/customization-deep-dive.md +319 -518
- package/skills/purgetss/references/dynamic-component-creation.md +33 -37
- package/skills/purgetss/references/grid-layout.md +42 -371
- package/skills/purgetss/references/icon-fonts.md +82 -475
- package/skills/purgetss/references/installation-setup.md +159 -331
- package/skills/purgetss/references/migration-guide.md +54 -109
- package/skills/purgetss/references/opacity-modifier.md +25 -222
- package/skills/purgetss/references/performance-tips.md +2 -2
- package/skills/purgetss/references/platform-modifiers.md +21 -407
- package/skills/purgetss/references/tikit-components.md +237 -104
- package/skills/purgetss/references/titanium-resets.md +20 -21
- package/skills/purgetss/references/ui-ux-design.md +171 -1702
- package/skills/ti-api/SKILL.md +109 -0
- package/skills/ti-api/references/api-android.md +675 -0
- package/skills/ti-api/references/api-app-platform.md +636 -0
- package/skills/ti-api/references/api-core.md +764 -0
- package/skills/ti-api/references/api-data-network.md +641 -0
- package/skills/ti-api/references/api-media.md +655 -0
- package/skills/ti-api/references/api-modules-ble-bluetooth.md +657 -0
- package/skills/ti-api/references/api-modules-coremotion-urlsession.md +411 -0
- package/skills/ti-api/references/api-modules-map.md +632 -0
- package/skills/ti-api/references/api-modules-nfc.md +725 -0
- package/skills/ti-api/references/api-modules-social-misc.md +526 -0
- package/skills/ti-api/references/api-services.md +700 -0
- package/skills/ti-api/references/api-ui-android.md +499 -0
- package/skills/ti-api/references/api-ui-extras.md +702 -0
- package/skills/ti-api/references/api-ui-ios-animator.md +378 -0
- package/skills/ti-api/references/api-ui-ios.md +756 -0
- package/skills/ti-api/references/api-ui-lists.md +581 -0
- package/skills/ti-api/references/api-ui-text-input.md +607 -0
- package/skills/ti-api/references/api-ui-views.md +572 -0
- package/skills/ti-api/references/api-ui-windows-navigation.md +676 -0
- package/skills/ti-api/references/api-xml-global.md +743 -0
- package/skills/ti-expert/SKILL.md +46 -45
- package/skills/ti-expert/references/adaptive-layouts.md +414 -0
- package/skills/ti-expert/references/alloy-builtins.md +16 -20
- package/skills/ti-expert/references/alloy-structure.md +40 -42
- package/skills/ti-expert/references/anti-patterns.md +34 -0
- package/skills/ti-expert/references/code-conventions.md +5 -3
- package/skills/ti-expert/references/error-handling.md +14 -7
- package/skills/ti-expert/references/examples.md +4 -2
- package/skills/ti-expert/references/performance-optimization.md +26 -24
- package/skills/ti-expert/references/security-device.md +17 -14
- package/skills/ti-expert/references/security-fundamentals.md +60 -101
- package/skills/ti-expert/references/state-management.md +15 -14
- package/skills/ti-expert/references/testing-e2e-ci.md +30 -21
- package/skills/ti-expert/references/theming.md +12 -20
- package/skills/ti-guides/SKILL.md +13 -17
- package/skills/ti-guides/references/advanced-data-and-images.md +30 -10
- package/skills/ti-guides/references/application-frameworks.md +3 -3
- package/skills/ti-guides/references/coding-best-practices.md +31 -2
- package/skills/ti-guides/references/commonjs-advanced.md +46 -4
- package/skills/ti-guides/references/hello-world.md +9 -13
- package/skills/ti-guides/references/hyperloop-native-access.md +4 -1
- package/skills/ti-guides/references/javascript-primer.md +13 -5
- package/skills/ti-guides/references/resources.md +0 -2
- package/skills/ti-guides/references/style-and-conventions.md +1 -0
- package/skills/ti-guides/references/tiapp-config.md +0 -32
- package/skills/ti-howtos/SKILL.md +43 -45
- package/skills/ti-howtos/references/buffer-codec-streams.md +25 -3
- package/skills/ti-howtos/references/debugging-profiling.md +14 -25
- package/skills/ti-howtos/references/google-maps-v2.md +3 -3
- package/skills/ti-howtos/references/ios-map-kit.md +8 -1
- package/skills/ti-howtos/references/notification-services.md +0 -1
- package/skills/ti-howtos/references/webpack-build-pipeline.md +3 -0
- package/skills/ti-ui/SKILL.md +47 -49
- package/skills/ti-ui/references/application-structures.md +3 -2
- package/skills/ti-ui/references/orientation.md +17 -9
- package/skills/ti-ui/references/platform-ui-ios.md +63 -0
- package/skills/ti-ui/references/scrolling-views.md +39 -0
|
@@ -1,969 +1,302 @@
|
|
|
1
|
-
# UI/UX Design Patterns for Titanium
|
|
1
|
+
# UI/UX Design Patterns for Titanium with PurgeTSS
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This guide collects practical UI patterns that stay aligned with Titanium layout behavior and verified PurgeTSS conventions.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Core Rules
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- PurgeTSS is not Tailwind CSS. Verify classes before recommending them.
|
|
8
|
+
- Titanium does not support Flexbox. Use `horizontal`, `vertical`, or the default composite layout.
|
|
9
|
+
- Do not use `p-*` on `View`, `Window`, `ScrollView`, or `TableView`. Use margins on children instead.
|
|
10
|
+
- Prefer `w-screen` over `w-full` when you need `Ti.UI.FILL`.
|
|
11
|
+
- For dynamic components, prefer `$.UI.create()` over manual `Ti.UI.create*()` styling.
|
|
8
12
|
|
|
9
|
-
Cards
|
|
13
|
+
## Cards
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
### Elevated Card
|
|
12
16
|
|
|
13
17
|
```xml
|
|
14
|
-
<View class="
|
|
15
|
-
<ImageView class="
|
|
16
|
-
<Label class="mb-2 text-xl font-bold text-gray-800" text="Card Title" />
|
|
17
|
-
<Label class="mb-4 text-sm text-gray-600" text="Card description goes here with details about the content." />
|
|
18
|
-
<View class="horizontal mt-4">
|
|
19
|
-
<Button class="bg-brand-500 mr-2 rounded-lg px-4 py-2 text-white" title="Action" />
|
|
20
|
-
<Button class="rounded-lg bg-gray-200 px-4 py-2 text-gray-700" title="Learn More" />
|
|
21
|
-
</View>
|
|
22
|
-
</View>
|
|
23
|
-
```
|
|
18
|
+
<View class="rounded-xl bg-white shadow-lg">
|
|
19
|
+
<ImageView class="h-48 w-screen rounded-t-xl" image="/images/card-image.jpg" />
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
<Label class="mx-4 mt-4 text-xl font-bold text-gray-800" text="Card Title" />
|
|
22
|
+
<Label class="mx-4 mt-2 text-sm text-gray-600" text="Card description goes here." />
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
<View class="horizontal mx-4 mt-4 mb-4">
|
|
25
|
+
<Button class="rounded-lg bg-blue-500 text-white" title="Action" />
|
|
26
|
+
<Button class="ml-2 rounded-lg bg-gray-200 text-gray-700" title="Learn More" />
|
|
27
|
+
</View>
|
|
31
28
|
</View>
|
|
32
29
|
```
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
### Outlined Card
|
|
35
32
|
|
|
36
33
|
```xml
|
|
37
|
-
<View class="
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
<Label class="mb-4 ml-4 text-xl font-bold text-white" text="Overlay Title" />
|
|
41
|
-
<Label class="mb-4 ml-4 text-sm text-gray-200" text="Subtitle text" />
|
|
42
|
-
</View>
|
|
34
|
+
<View class="rounded-xl border-(1) border-gray-300 bg-white">
|
|
35
|
+
<Label class="mx-4 mt-4 text-lg font-semibold text-gray-800" text="Outlined Card" />
|
|
36
|
+
<Label class="mx-4 mt-2 mb-4 h-auto text-sm text-gray-600" text="Clean borders without elevation." />
|
|
43
37
|
</View>
|
|
44
38
|
```
|
|
45
39
|
|
|
46
|
-
|
|
40
|
+
### Card with Image Overlay
|
|
47
41
|
|
|
48
42
|
```xml
|
|
49
|
-
<View class="
|
|
50
|
-
<ImageView class="
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<Label class="text-
|
|
54
|
-
<Label class="
|
|
43
|
+
<View class="h-48 w-screen rounded-xl">
|
|
44
|
+
<ImageView class="h-48 w-screen rounded-xl" image="/images/cover.jpg" />
|
|
45
|
+
|
|
46
|
+
<View class="bottom-0 w-screen bg-(#88000000) rounded-b-xl">
|
|
47
|
+
<Label class="mx-4 mt-4 text-xl font-bold text-white" text="Overlay Title" />
|
|
48
|
+
<Label class="mx-4 mt-2 mb-4 h-auto text-sm text-gray-200" text="Subtitle text" />
|
|
55
49
|
</View>
|
|
56
50
|
</View>
|
|
57
51
|
```
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Lists display multiple items vertically and are essential for mobile UIs.
|
|
53
|
+
## Lists
|
|
62
54
|
|
|
63
|
-
|
|
55
|
+
### Simple List Item
|
|
64
56
|
|
|
65
57
|
```xml
|
|
66
58
|
<View class="border-b border-gray-200 bg-white">
|
|
67
|
-
<Label class="mx-4 my-4 text-base text-gray-800" text="List item content" />
|
|
59
|
+
<Label class="mx-4 my-4 h-auto text-base text-gray-800" text="List item content" />
|
|
68
60
|
</View>
|
|
69
61
|
```
|
|
70
62
|
|
|
71
|
-
|
|
63
|
+
### List with Avatar
|
|
72
64
|
|
|
73
65
|
```xml
|
|
74
66
|
<View class="horizontal border-b border-gray-200 bg-white">
|
|
75
|
-
<View class="
|
|
67
|
+
<View class="ml-4 my-4 rounded-full-12 bg-blue-500">
|
|
76
68
|
<Label class="center text-lg font-bold text-white" text="JD" />
|
|
77
69
|
</View>
|
|
78
|
-
|
|
70
|
+
|
|
71
|
+
<View class="vertical ml-3 my-4">
|
|
79
72
|
<Label class="text-base font-semibold text-gray-800" text="John Doe" />
|
|
80
73
|
<Label class="text-sm text-gray-500" text="john.doe@example.com" />
|
|
81
74
|
</View>
|
|
82
75
|
</View>
|
|
83
76
|
```
|
|
84
77
|
|
|
85
|
-
|
|
78
|
+
### ListView Template
|
|
86
79
|
|
|
87
80
|
```xml
|
|
88
|
-
<
|
|
89
|
-
<View class="horizontal">
|
|
90
|
-
<View class="horizontal">
|
|
91
|
-
<View class="ml-4 mr-4 h-12 w-12 rounded-lg bg-blue-100">
|
|
92
|
-
<Label class="center fas fa-folder text-xl text-blue-500" />
|
|
93
|
-
</View>
|
|
94
|
-
<View class="vertical">
|
|
95
|
-
<Label class="text-base font-semibold text-gray-800" text="Project Folder" />
|
|
96
|
-
<Label class="text-sm text-gray-500" text="24 files" />
|
|
97
|
-
</View>
|
|
98
|
-
</View>
|
|
99
|
-
<Label class="fas fa-chevron-right mr-4 text-gray-400" />
|
|
100
|
-
</View>
|
|
101
|
-
</View>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
#### List with Dividers
|
|
105
|
-
|
|
106
|
-
```xml
|
|
107
|
-
<View class="bg-white">
|
|
108
|
-
<!-- Item 1 -->
|
|
109
|
-
<View class="border-b border-gray-100">
|
|
110
|
-
<Label class="mx-4 my-4 text-base text-gray-800" text="Item 1" />
|
|
111
|
-
</View>
|
|
112
|
-
<!-- Item 2 -->
|
|
113
|
-
<View class="border-b border-gray-100">
|
|
114
|
-
<Label class="mx-4 my-4 text-base text-gray-800" text="Item 2" />
|
|
115
|
-
</View>
|
|
116
|
-
<!-- Item 3 -->
|
|
117
|
-
<View>
|
|
118
|
-
<Label class="mx-4 my-4 text-base text-gray-800" text="Item 3" />
|
|
119
|
-
</View>
|
|
120
|
-
</View>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
#### ListView with Custom Template
|
|
124
|
-
|
|
125
|
-
```xml
|
|
126
|
-
<ListView id="myList" defaultItemTemplate="cardTemplate">
|
|
81
|
+
<ListView id="myList">
|
|
127
82
|
<Templates>
|
|
128
|
-
<ItemTemplate name="cardTemplate" class="h-
|
|
129
|
-
<View bindId="container" class="mx-4
|
|
130
|
-
<View class="
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</View>
|
|
83
|
+
<ItemTemplate name="cardTemplate" class="h-20">
|
|
84
|
+
<View bindId="container" class="horizontal mx-4 my-2 rounded-lg bg-white shadow-lg">
|
|
85
|
+
<View bindId="iconWrap" class="ml-4 my-4 rounded-full-10 bg-blue-100">
|
|
86
|
+
<Label bindId="icon" class="center fas fa-home text-blue-500" />
|
|
87
|
+
</View>
|
|
88
|
+
|
|
89
|
+
<View class="vertical ml-3 my-4">
|
|
90
|
+
<Label bindId="title" class="text-base font-semibold text-gray-800" />
|
|
91
|
+
<Label bindId="subtitle" class="text-sm text-gray-500" />
|
|
138
92
|
</View>
|
|
139
93
|
</View>
|
|
140
94
|
</ItemTemplate>
|
|
141
95
|
</Templates>
|
|
142
|
-
<ListSection/>
|
|
143
|
-
</ListView>
|
|
144
|
-
```
|
|
145
96
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const items = [
|
|
149
|
-
{
|
|
150
|
-
container: {},
|
|
151
|
-
icon: { text: "\uf015" }, // fa-home
|
|
152
|
-
title: { text: "Item 1" },
|
|
153
|
-
subtitle: { text: "First item description" }
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
container: {},
|
|
157
|
-
icon: { text: "\uf007" }, // fa-user
|
|
158
|
-
title: { text: "Item 2" },
|
|
159
|
-
subtitle: { text: "Second item description" }
|
|
160
|
-
}
|
|
161
|
-
]
|
|
162
|
-
|
|
163
|
-
$.myList.sections[0].setItems(items)
|
|
97
|
+
<ListSection />
|
|
98
|
+
</ListView>
|
|
164
99
|
```
|
|
165
100
|
|
|
166
|
-
|
|
101
|
+
> **⚠️ ListView Performance**
|
|
102
|
+
> Prefer fixed item heights for production ListView templates. Avoid relying on `Ti.UI.SIZE` in scrolling cells when performance matters.
|
|
167
103
|
|
|
168
|
-
Forms
|
|
104
|
+
## Forms
|
|
169
105
|
|
|
170
|
-
|
|
106
|
+
### Text Input
|
|
171
107
|
|
|
172
108
|
```xml
|
|
173
109
|
<View class="mb-4">
|
|
174
110
|
<Label class="mb-2 text-sm font-medium text-gray-700" text="Email Address" />
|
|
175
|
-
<TextField class="
|
|
111
|
+
<TextField class="w-screen rounded-lg border-(1) border-gray-300 bg-white px-4 py-3 text-base text-gray-800" hintText="you@example.com" keyboardType="Ti.UI.KEYBOARD_TYPE_EMAIL" autocapitalization="Ti.UI.TEXT_AUTOCAPITALIZATION_NONE" autocorrect="false" />
|
|
176
112
|
</View>
|
|
177
113
|
```
|
|
178
114
|
|
|
179
|
-
|
|
115
|
+
### Text Area
|
|
180
116
|
|
|
181
117
|
```xml
|
|
182
118
|
<View class="mb-4">
|
|
183
119
|
<Label class="mb-2 text-sm font-medium text-gray-700" text="Message" />
|
|
184
|
-
<TextArea class="h-32 w-
|
|
185
|
-
</View>
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
#### Password Input
|
|
189
|
-
|
|
190
|
-
```xml
|
|
191
|
-
<View class="mb-4">
|
|
192
|
-
<Label class="mb-2 text-sm font-medium text-gray-700" text="Password" />
|
|
193
|
-
<TextField class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-base text-gray-800" passwordMask="true" hintText="Enter password" />
|
|
194
|
-
</View>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
#### Select/Dropdown (using Picker)
|
|
198
|
-
|
|
199
|
-
```xml
|
|
200
|
-
<View class="mb-4">
|
|
201
|
-
<Label class="mb-2 text-sm font-medium text-gray-700" text="Category" />
|
|
202
|
-
<Picker class="w-full rounded-lg border border-gray-300 bg-white" selectionIndicator="true">
|
|
203
|
-
<PickerRow title="Option 1" />
|
|
204
|
-
<PickerRow title="Option 2" />
|
|
205
|
-
<PickerRow title="Option 3" />
|
|
206
|
-
</Picker>
|
|
120
|
+
<TextArea class="h-32 w-screen rounded-lg border-(1) border-gray-300 bg-white px-4 py-3 text-base text-gray-800" hintText="Type your message here..." />
|
|
207
121
|
</View>
|
|
208
122
|
```
|
|
209
123
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
```xml
|
|
213
|
-
<View class="horizontal mb-4 rounded-lg border border-gray-200 bg-white">
|
|
214
|
-
<View class="vertical">
|
|
215
|
-
<Label class="text-base font-medium text-gray-800" text="Notifications" />
|
|
216
|
-
<Label class="text-sm text-gray-500" text="Receive push notifications" />
|
|
217
|
-
</View>
|
|
218
|
-
<Switch class="ml-4" value="true" />
|
|
219
|
-
</View>
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
#### Slider
|
|
124
|
+
### Validation State
|
|
223
125
|
|
|
224
126
|
```xml
|
|
225
127
|
<View class="mb-4">
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
</View>
|
|
230
|
-
<Slider class="w-full" min="0" max="100" value="50" />
|
|
231
|
-
</View>
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
#### Form with Validation States
|
|
235
|
-
|
|
236
|
-
```xml
|
|
237
|
-
<!-- Error State -->
|
|
238
|
-
<View class="mb-4">
|
|
239
|
-
<Label class="mb-2 text-sm font-medium text-red-600" text="Email Address" />
|
|
240
|
-
<TextField class="w-full rounded-lg border border-red-500 bg-white px-4 py-3 text-base" hintText="Invalid email" />
|
|
241
|
-
<Label class="mt-1 text-xs text-red-500" text="Please enter a valid email address" />
|
|
242
|
-
</View>
|
|
243
|
-
|
|
244
|
-
<!-- Success State -->
|
|
245
|
-
<View class="mb-4">
|
|
246
|
-
<Label class="mb-2 text-sm font-medium text-green-600" text="Email Address" />
|
|
247
|
-
<TextField class="w-full rounded-lg border border-green-500 bg-white px-4 py-3 text-base" hintText="Valid email" />
|
|
248
|
-
<Label class="fas fa-check-circle mt-1 text-xs text-green-500" text=" Valid email" />
|
|
249
|
-
</View>
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
#### Form Section
|
|
253
|
-
|
|
254
|
-
```xml
|
|
255
|
-
<View class="mb-6 rounded-lg bg-white shadow">
|
|
256
|
-
<Label class="mx-6 mb-6 mt-6 text-xl font-bold text-gray-800" text="Account Information" />
|
|
257
|
-
|
|
258
|
-
<View class="mb-4">
|
|
259
|
-
<Label class="mb-2 text-sm font-medium text-gray-700" text="Full Name" />
|
|
260
|
-
<TextField class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3" hintText="John Doe" />
|
|
261
|
-
</View>
|
|
262
|
-
|
|
263
|
-
<View class="mb-4">
|
|
264
|
-
<Label class="mb-2 text-sm font-medium text-gray-700" text="Email" />
|
|
265
|
-
<TextField class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3" hintText="john@example.com" keyboardType="Ti.UI.KEYBOARD_TYPE_EMAIL" />
|
|
266
|
-
</View>
|
|
267
|
-
|
|
268
|
-
<View class="mb-6">
|
|
269
|
-
<Label class="mb-2 text-sm font-medium text-gray-700" text="Phone" />
|
|
270
|
-
<TextField class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3" hintText="+1 (555) 000-0000" keyboardType="Ti.UI.KEYBOARD_TYPE_PHONE" />
|
|
271
|
-
</View>
|
|
272
|
-
|
|
273
|
-
<Button class="bg-brand-500 w-full rounded-lg py-3 text-base font-semibold text-white" title="Save Changes" />
|
|
274
|
-
</View>
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### Buttons
|
|
278
|
-
|
|
279
|
-
Buttons trigger actions and are primary interactive elements.
|
|
280
|
-
|
|
281
|
-
#### Primary Button
|
|
282
|
-
|
|
283
|
-
```xml
|
|
284
|
-
<Button class="bg-brand-500 rounded-lg px-6 py-3 text-base font-semibold text-white shadow-md" title="Primary Action" />
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
#### Secondary Button
|
|
288
|
-
|
|
289
|
-
```xml
|
|
290
|
-
<Button class="rounded-lg border border-gray-300 bg-white px-6 py-3 text-base font-semibold text-gray-700" title="Secondary Action" />
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
#### Outline Button
|
|
294
|
-
|
|
295
|
-
```xml
|
|
296
|
-
<Button class="border-brand-500 text-brand-500 rounded-lg border-2 bg-transparent px-6 py-3 text-base font-semibold" title="Outline" />
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
#### Text Button
|
|
300
|
-
|
|
301
|
-
```xml
|
|
302
|
-
<Button class="text-brand-500 bg-transparent text-base font-semibold" title="Text Only" />
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
#### Icon Button
|
|
306
|
-
|
|
307
|
-
```xml
|
|
308
|
-
<!-- EFFICIENT: Single Label with icon, size, and styling -->
|
|
309
|
-
<Label class="rounded-full-12 bg-brand-500 fa-solid fa-plus text-center text-xl text-white" />
|
|
310
|
-
|
|
311
|
-
<!-- ALTERNATIVE: View + Label (more verbose) -->
|
|
312
|
-
<View class="bg-brand-500 h-12 w-12 rounded-full">
|
|
313
|
-
<Label class="center fas fa-plus text-xl text-white" />
|
|
128
|
+
<Label class="mb-2 text-sm font-medium text-gray-700" text="Email Address" />
|
|
129
|
+
<TextField class="w-screen rounded-lg border-(1) border-red-500 bg-white px-4 py-3 text-base text-gray-800" hintText="Invalid email" />
|
|
130
|
+
<Label class="mt-1 text-xs text-red-500" text="Please enter a valid email address." />
|
|
314
131
|
</View>
|
|
315
132
|
```
|
|
316
133
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
- `rounded-full-XX` (includes size) - Creates the circle and sets dimensions
|
|
320
|
-
- Font Awesome icon class (e.g., `fa-solid fa-plus`)
|
|
321
|
-
- `text-center` - Centers the icon
|
|
322
|
-
- Background color and text color
|
|
134
|
+
> **⚠️ Input Padding**
|
|
135
|
+
> Padding utilities (`p-`, `px-`, `py-`) work on `TextField` and `TextArea`, but NOT on `Button` (especially iOS/Mac) and NOT on container views (`View`, `Window`, `ScrollView`, `TableView`). Use margins on children to simulate internal spacing on containers.
|
|
323
136
|
|
|
324
|
-
|
|
325
|
-
:::
|
|
137
|
+
## Buttons
|
|
326
138
|
|
|
327
|
-
|
|
139
|
+
### Primary Button
|
|
328
140
|
|
|
329
141
|
```xml
|
|
330
|
-
|
|
331
|
-
<Label class="rounded-full-14 bg-brand-500 fa-solid fa-plus absolute bottom-6 right-6 text-center text-2xl text-white shadow-lg" />
|
|
332
|
-
|
|
333
|
-
<!-- ALTERNATIVE: View wrapper approach -->
|
|
334
|
-
<View class="absolute bottom-6 right-6">
|
|
335
|
-
<View class="bg-brand-500 h-14 w-14 rounded-full shadow-lg">
|
|
336
|
-
<Label class="center fas fa-plus text-2xl text-white" />
|
|
337
|
-
</View>
|
|
338
|
-
</View>
|
|
142
|
+
<Button class="rounded-lg bg-blue-500 text-base font-semibold text-white" title="Save Changes" />
|
|
339
143
|
```
|
|
340
144
|
|
|
341
|
-
|
|
145
|
+
### Secondary Button
|
|
342
146
|
|
|
343
147
|
```xml
|
|
344
|
-
<
|
|
345
|
-
<Label class="fas fa-arrow-left mr-2 text-lg text-white" />
|
|
346
|
-
<Label class="text-base font-semibold text-white" text="Back" />
|
|
347
|
-
</View>
|
|
148
|
+
<Button class="rounded-lg bg-gray-200 text-base font-semibold text-gray-800" title="Cancel" />
|
|
348
149
|
```
|
|
349
150
|
|
|
350
|
-
|
|
151
|
+
### Outline Button
|
|
351
152
|
|
|
352
153
|
```xml
|
|
353
|
-
<
|
|
354
|
-
<View class="w-1/3 rounded-md bg-white py-2 shadow-sm">
|
|
355
|
-
<Label class="text-center text-sm font-semibold text-gray-800" text="Day" />
|
|
356
|
-
</View>
|
|
357
|
-
<View class="w-1/3 py-2">
|
|
358
|
-
<Label class="text-center text-sm font-medium text-gray-500" text="Week" />
|
|
359
|
-
</View>
|
|
360
|
-
<View class="w-1/3 py-2">
|
|
361
|
-
<Label class="text-center text-sm font-medium text-gray-500" text="Month" />
|
|
362
|
-
</View>
|
|
363
|
-
</View>
|
|
154
|
+
<Button class="rounded-lg border-(1) border-blue-500 text-base font-semibold text-blue-500" title="Learn More" />
|
|
364
155
|
```
|
|
365
156
|
|
|
366
|
-
|
|
157
|
+
### Icon Button
|
|
367
158
|
|
|
368
159
|
```xml
|
|
369
|
-
|
|
370
|
-
<
|
|
371
|
-
|
|
372
|
-
<!-- Medium (Default) -->
|
|
373
|
-
<Button class="bg-brand-500 rounded-lg px-6 py-3 text-base font-semibold text-white" title="Medium" />
|
|
374
|
-
|
|
375
|
-
<!-- Large -->
|
|
376
|
-
<Button class="bg-brand-500 rounded-xl px-8 py-4 text-lg font-semibold text-white" title="Large" />
|
|
160
|
+
<Button class="rounded-lg bg-blue-500 text-white" title="Save" />
|
|
161
|
+
<Label class="fas fa-save ml-2 text-white" />
|
|
377
162
|
```
|
|
378
163
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
Navigation bars provide hierarchy and navigation context.
|
|
164
|
+
## Navigation Patterns
|
|
382
165
|
|
|
383
|
-
|
|
166
|
+
### Top Bar with Title and Action
|
|
384
167
|
|
|
385
168
|
```xml
|
|
386
|
-
<View class="
|
|
387
|
-
<Label class="
|
|
388
|
-
<Label class="
|
|
389
|
-
<Label class="fas fa-ellipsis-v mr-4 text-xl text-gray-600" />
|
|
169
|
+
<View class="h-14 w-screen bg-white border-b border-gray-200">
|
|
170
|
+
<Label class="left-4 center text-lg font-bold text-gray-800" text="Dashboard" />
|
|
171
|
+
<Label class="right-4 center fas fa-bars text-gray-700" />
|
|
390
172
|
</View>
|
|
391
173
|
```
|
|
392
174
|
|
|
393
|
-
|
|
175
|
+
### Bottom Tab Bar
|
|
394
176
|
|
|
395
177
|
```xml
|
|
396
|
-
<View class="bg-
|
|
397
|
-
<Label class="fas fa-bars ml-4 text-xl text-white" />
|
|
398
|
-
<Label class="w-screen text-center text-lg font-semibold text-white" text="Dashboard" />
|
|
399
|
-
<View class="horizontal mr-4">
|
|
400
|
-
<Label class="fas fa-search mr-4 text-xl text-white" />
|
|
401
|
-
<Label class="fas fa-bell text-xl text-white" />
|
|
402
|
-
</View>
|
|
403
|
-
</View>
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
#### Tab Bar (Bottom)
|
|
407
|
-
|
|
408
|
-
```xml
|
|
409
|
-
<View class="horizontal h-16 border-t border-gray-200 bg-white">
|
|
178
|
+
<View class="bottom-0 h-16 w-screen bg-white border-t border-gray-200 horizontal">
|
|
410
179
|
<View class="vertical w-1/3">
|
|
411
|
-
<Label class="fas fa-home text-
|
|
412
|
-
<Label class="
|
|
180
|
+
<Label class="mx-auto mt-2 fas fa-home text-blue-500" />
|
|
181
|
+
<Label class="mx-auto mt-1 text-xs text-blue-500" text="Home" />
|
|
413
182
|
</View>
|
|
183
|
+
|
|
414
184
|
<View class="vertical w-1/3">
|
|
415
|
-
<Label class="
|
|
416
|
-
<Label class="
|
|
185
|
+
<Label class="mx-auto mt-2 fas fa-search text-gray-400" />
|
|
186
|
+
<Label class="mx-auto mt-1 text-xs text-gray-400" text="Search" />
|
|
417
187
|
</View>
|
|
188
|
+
|
|
418
189
|
<View class="vertical w-1/3">
|
|
419
|
-
<Label class="
|
|
420
|
-
<Label class="
|
|
190
|
+
<Label class="mx-auto mt-2 fas fa-user text-gray-400" />
|
|
191
|
+
<Label class="mx-auto mt-1 text-xs text-gray-400" text="Profile" />
|
|
421
192
|
</View>
|
|
422
193
|
</View>
|
|
423
194
|
```
|
|
424
195
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
```xml
|
|
428
|
-
<View class="horizontal mx-4 my-4 rounded-lg bg-gray-200">
|
|
429
|
-
<View id="segment1" class="w-1/2 rounded-md bg-white py-2 shadow-sm">
|
|
430
|
-
<Label class="text-center text-sm font-semibold text-gray-800" text="Active" />
|
|
431
|
-
</View>
|
|
432
|
-
<View id="segment2" class="w-1/2 py-2">
|
|
433
|
-
<Label class="text-center text-sm font-medium text-gray-500" text="Inactive" />
|
|
434
|
-
</View>
|
|
435
|
-
</View>
|
|
436
|
-
```
|
|
196
|
+
## Modals, Sheets, and Feedback
|
|
437
197
|
|
|
438
|
-
|
|
198
|
+
### Full-Screen Overlay
|
|
439
199
|
|
|
440
200
|
```xml
|
|
441
|
-
<View class="
|
|
442
|
-
<
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
<Label class="fas fa-chevron-right mx-2 my-3 text-xs text-gray-400" />
|
|
446
|
-
<Label class="my-3 text-sm font-medium text-gray-800" text="Category" />
|
|
447
|
-
</View>
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### Modals and Dialogs
|
|
451
|
-
|
|
452
|
-
Modals focus user attention on specific content or actions.
|
|
453
|
-
|
|
454
|
-
#### Alert Dialog
|
|
201
|
+
<View class="h-screen w-screen bg-(#88000000)">
|
|
202
|
+
<View class="mx-6 mt-24 rounded-xl bg-white shadow-lg">
|
|
203
|
+
<Label class="mx-4 mt-4 text-lg font-bold text-gray-800" text="Confirm Action" />
|
|
204
|
+
<Label class="mx-4 mt-2 text-sm text-gray-600" text="Are you sure you want to continue?" />
|
|
455
205
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
title: 'Delete Item',
|
|
460
|
-
buttonNames: ['Cancel', 'Delete'],
|
|
461
|
-
message: 'Are you sure you want to delete this item? This action cannot be undone.'
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
dialog.addEventListener('click', (e) => {
|
|
465
|
-
if (e.index === 1) {
|
|
466
|
-
// Delete action
|
|
467
|
-
}
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
dialog.show()
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
#### Full Screen Modal (iOS)
|
|
474
|
-
|
|
475
|
-
```xml
|
|
476
|
-
<!-- modal.xml -->
|
|
477
|
-
<Window class="bg-white" modal="true">
|
|
478
|
-
<View class="vertical h-full">
|
|
479
|
-
<!-- Header -->
|
|
480
|
-
<View class="horizontal border-b border-gray-200 bg-white">
|
|
481
|
-
<Label id="closeBtn" class="text-brand-500 text-base font-semibold" text="Cancel" />
|
|
482
|
-
<Label class="w-screen text-center text-lg font-semibold text-gray-800" text="Modal Title" />
|
|
483
|
-
<Label id="saveBtn" class="text-brand-500 text-base font-semibold" text="Save" />
|
|
206
|
+
<View class="horizontal mx-4 mt-4 mb-4">
|
|
207
|
+
<Button class="rounded-lg bg-gray-200 text-gray-700" title="Cancel" />
|
|
208
|
+
<Button class="ml-2 rounded-lg bg-red-500 text-white" title="Delete" />
|
|
484
209
|
</View>
|
|
485
|
-
|
|
486
|
-
<!-- Content -->
|
|
487
|
-
<ScrollView class="h-full">
|
|
488
|
-
<View>
|
|
489
|
-
<Label class="mx-4 my-4 text-base text-gray-800" text="Modal content goes here..." />
|
|
490
|
-
</View>
|
|
491
|
-
</ScrollView>
|
|
492
210
|
</View>
|
|
493
|
-
</
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
```javascript
|
|
497
|
-
// Open modal
|
|
498
|
-
const modal = Alloy.createController('modal').getView()
|
|
499
|
-
modal.open({ modal: true })
|
|
211
|
+
</View>
|
|
500
212
|
```
|
|
501
213
|
|
|
502
|
-
|
|
214
|
+
### Bottom Sheet
|
|
503
215
|
|
|
504
216
|
```xml
|
|
505
|
-
<View class="
|
|
506
|
-
<View class="mx-auto
|
|
507
|
-
<
|
|
508
|
-
|
|
509
|
-
<
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
</View>
|
|
513
|
-
<View class="border-b border-gray-100">
|
|
514
|
-
<Label class="mx-4 my-3 text-base text-gray-800" text="Action 2" />
|
|
515
|
-
</View>
|
|
516
|
-
<View>
|
|
517
|
-
<Label class="mx-4 my-3 text-base font-semibold text-red-500" text="Delete" />
|
|
518
|
-
</View>
|
|
519
|
-
</View>
|
|
217
|
+
<View class="bottom-0 w-screen rounded-t-xl bg-white shadow-lg">
|
|
218
|
+
<View class="mx-auto mt-2 h-1 w-10 rounded bg-gray-300" />
|
|
219
|
+
<Label class="mx-4 mt-4 text-lg font-bold text-gray-800" text="Share" />
|
|
220
|
+
<View class="vertical mx-4 mt-4 mb-4">
|
|
221
|
+
<Label class="text-base text-gray-700" text="Copy link" />
|
|
222
|
+
<Label class="mt-3 text-base text-gray-700" text="Send message" />
|
|
223
|
+
<Label class="mt-3 text-base text-gray-700" text="Open in browser" />
|
|
520
224
|
</View>
|
|
521
225
|
</View>
|
|
522
226
|
```
|
|
523
227
|
|
|
524
|
-
###
|
|
525
|
-
|
|
526
|
-
Brief notifications that provide feedback.
|
|
527
|
-
|
|
528
|
-
#### Simple Toast
|
|
529
|
-
|
|
530
|
-
```javascript
|
|
531
|
-
// Native toast (Android only)
|
|
532
|
-
var toast = Ti.UI.createNotification({
|
|
533
|
-
message: "Operation successful",
|
|
534
|
-
duration: Ti.UI.NOTIFICATION_DURATION_SHORT
|
|
535
|
-
})
|
|
536
|
-
toast.show()
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
#### Custom Snackbar
|
|
228
|
+
### Snackbar
|
|
540
229
|
|
|
541
230
|
```xml
|
|
542
|
-
<View id="snackbar" class="
|
|
231
|
+
<View id="snackbar" class="bottom-4 left-4 right-4 hidden rounded-lg bg-gray-800 shadow-lg">
|
|
543
232
|
<View class="horizontal">
|
|
544
|
-
<Label class="
|
|
545
|
-
<Label id="undoBtn" class="
|
|
233
|
+
<Label class="mx-4 my-4 h-auto text-base text-white" text="File deleted successfully" />
|
|
234
|
+
<Label id="undoBtn" class="my-4 mr-4 h-auto text-base font-semibold text-blue-400" text="UNDO" />
|
|
546
235
|
</View>
|
|
547
236
|
</View>
|
|
548
237
|
```
|
|
549
238
|
|
|
550
|
-
|
|
551
|
-
// Show snackbar
|
|
552
|
-
$.snackbar.setVisible(true)
|
|
553
|
-
setTimeout(() => {
|
|
554
|
-
$.snackbar.setVisible(false)
|
|
555
|
-
}, 3000)
|
|
556
|
-
```
|
|
239
|
+
## Layout Patterns
|
|
557
240
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
```xml
|
|
561
|
-
<View class="horizontal absolute left-4 right-4 top-4 rounded-lg bg-green-500 shadow-lg">
|
|
562
|
-
<Label class="fas fa-check-circle mr-3 text-xl text-white" />
|
|
563
|
-
<Label class="text-base font-medium text-white" text="Changes saved successfully!" />
|
|
564
|
-
</View>
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
#### Error Toast
|
|
568
|
-
|
|
569
|
-
```xml
|
|
570
|
-
<View class="horizontal absolute left-4 right-4 top-4 rounded-lg bg-red-500 shadow-lg">
|
|
571
|
-
<Label class="fas fa-exclamation-circle mr-3 text-xl text-white" />
|
|
572
|
-
<Label class="text-base font-medium text-white" text="An error occurred. Please try again." />
|
|
573
|
-
</View>
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
---
|
|
577
|
-
|
|
578
|
-
## 2. Layout Patterns
|
|
579
|
-
|
|
580
|
-
### Screen Structure
|
|
581
|
-
|
|
582
|
-
#### Standard Screen Layout
|
|
241
|
+
### Standard Screen Layout
|
|
583
242
|
|
|
584
243
|
```xml
|
|
585
244
|
<Window class="bg-gray-100">
|
|
586
|
-
<
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
<Label class="mx-4 my-4 text-2xl font-bold text-gray-800" text="Screen Title" />
|
|
590
|
-
</View>
|
|
591
|
-
|
|
592
|
-
<!-- Content -->
|
|
593
|
-
<View>
|
|
594
|
-
<!-- Main content goes here -->
|
|
245
|
+
<View class="vertical h-screen w-screen">
|
|
246
|
+
<View class="h-14 w-screen border-b border-gray-200 bg-white">
|
|
247
|
+
<Label class="left-4 center text-xl font-bold text-gray-800" text="Screen Title" />
|
|
595
248
|
</View>
|
|
596
249
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
</Window>
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
#### Tab Bar Screen Structure
|
|
606
|
-
|
|
607
|
-
```xml
|
|
608
|
-
<TabGroup>
|
|
609
|
-
<!-- Tab 1 -->
|
|
610
|
-
<Tab>
|
|
611
|
-
<Window class="bg-gray-100">
|
|
612
|
-
<ScrollView class="h-full w-full">
|
|
613
|
-
<Label class="mb-4 text-lg font-semibold text-gray-800" text="Home" />
|
|
614
|
-
<!-- Content -->
|
|
615
|
-
</ScrollView>
|
|
616
|
-
</Window>
|
|
617
|
-
</Tab>
|
|
618
|
-
|
|
619
|
-
<!-- Tab 2 -->
|
|
620
|
-
<Tab>
|
|
621
|
-
<Window class="bg-gray-100">
|
|
622
|
-
<ScrollView class="h-full w-full">
|
|
623
|
-
<Label class="mb-4 text-lg font-semibold text-gray-800" text="Search" />
|
|
624
|
-
<!-- Content -->
|
|
625
|
-
</ScrollView>
|
|
626
|
-
</Window>
|
|
627
|
-
</Tab>
|
|
628
|
-
|
|
629
|
-
<!-- Tab 3 -->
|
|
630
|
-
<Tab>
|
|
631
|
-
<Window class="bg-gray-100">
|
|
632
|
-
<ScrollView class="h-full w-full">
|
|
633
|
-
<Label class="mb-4 text-lg font-semibold text-gray-800" text="Profile" />
|
|
634
|
-
<!-- Content -->
|
|
635
|
-
</ScrollView>
|
|
636
|
-
</Window>
|
|
637
|
-
</Tab>
|
|
638
|
-
</TabGroup>
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### Spacing System
|
|
642
|
-
|
|
643
|
-
PurgeTSS uses a consistent spacing scale based on 4px increments:
|
|
644
|
-
|
|
645
|
-
| Class | Spacing | Usage |
|
|
646
|
-
| ----- | ------- | ----------- |
|
|
647
|
-
| `p-0` | 0px | No padding |
|
|
648
|
-
| `p-1` | 4px | Extra small |
|
|
649
|
-
| `p-2` | 8px | Small |
|
|
650
|
-
| `p-3` | 12px | Medium |
|
|
651
|
-
| `p-4` | 16px | Standard |
|
|
652
|
-
| `p-5` | 20px | Large |
|
|
653
|
-
| `p-6` | 24px | Extra large |
|
|
654
|
-
| `p-8` | 32px | Double |
|
|
655
|
-
|
|
656
|
-
**Note:** Padding classes (`p-*`) work on some elements like TextField, TextArea, Button, and Label, but do NOT work on View. For View spacing, use margin classes on child elements instead.
|
|
657
|
-
|
|
658
|
-
#### Margin Examples
|
|
659
|
-
|
|
660
|
-
```xml
|
|
661
|
-
<!-- All sides -->
|
|
662
|
-
<View class="m-4">...</View>
|
|
663
|
-
|
|
664
|
-
<!-- Horizontal -->
|
|
665
|
-
<View class="mx-4">...</View>
|
|
666
|
-
|
|
667
|
-
<!-- Vertical -->
|
|
668
|
-
<View class="my-4">...</View>
|
|
669
|
-
|
|
670
|
-
<!-- Individual sides -->
|
|
671
|
-
<View class="mb-4 ml-2 mr-4 mt-2">...</View>
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
#### Padding Examples
|
|
675
|
-
|
|
676
|
-
```xml
|
|
677
|
-
<!-- All sides (works on TextField, TextArea, Button, Label) -->
|
|
678
|
-
<TextField class="p-4" hintText="Input with padding" />
|
|
679
|
-
|
|
680
|
-
<!-- Horizontal (works on TextField, TextArea, Button, Label) -->
|
|
681
|
-
<TextField class="px-4" hintText="Input with horizontal padding" />
|
|
682
|
-
|
|
683
|
-
<!-- Vertical (works on TextField, TextArea, Button, Label) -->
|
|
684
|
-
<TextField class="py-4" hintText="Input with vertical padding" />
|
|
685
|
-
|
|
686
|
-
<!-- Individual sides (works on TextField, TextArea, Button, Label) -->
|
|
687
|
-
<TextField class="pb-4 pl-2 pr-4 pt-2" hintText="Input with individual padding" />
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
#### Gap (Spacing between children)
|
|
691
|
-
|
|
692
|
-
```xml
|
|
693
|
-
<View class="gap-2"> <!-- 8px between all children -->
|
|
694
|
-
<View class="h-10 bg-red-500" />
|
|
695
|
-
<View class="h-10 bg-blue-500" />
|
|
696
|
-
<View class="h-10 bg-green-500" />
|
|
697
|
-
</View>
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
### Responsive Patterns
|
|
701
|
-
|
|
702
|
-
#### Responsive Grid
|
|
703
|
-
|
|
704
|
-
```xml
|
|
705
|
-
<!-- Two columns on large screens, one column on small -->
|
|
706
|
-
<View class="grid">
|
|
707
|
-
<View class="col-span-6 md:col-span-12">
|
|
708
|
-
<Label text="Responsive Item" />
|
|
250
|
+
<ScrollView class="h-screen w-screen">
|
|
251
|
+
<View class="vertical">
|
|
252
|
+
<Label class="mx-4 mt-4 text-base text-gray-700" text="Main content goes here." />
|
|
253
|
+
</View>
|
|
254
|
+
</ScrollView>
|
|
709
255
|
</View>
|
|
710
|
-
</
|
|
256
|
+
</Window>
|
|
711
257
|
```
|
|
712
258
|
|
|
713
|
-
|
|
259
|
+
### Two-Column Tablet Layout
|
|
714
260
|
|
|
715
261
|
```xml
|
|
716
|
-
|
|
717
|
-
<View class="
|
|
718
|
-
|
|
719
|
-
<View class="w-1/4 md:w-full">
|
|
720
|
-
<Label text="Sidebar" />
|
|
262
|
+
<View class="horizontal w-screen">
|
|
263
|
+
<View class="tablet:w-(30%) handheld:w-screen bg-white">
|
|
264
|
+
<Label class="mx-4 my-4 text-base font-semibold text-gray-800" text="Sidebar" />
|
|
721
265
|
</View>
|
|
722
266
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
<Label text="Main Content" />
|
|
267
|
+
<View class="tablet:w-(70%) handheld:w-screen bg-gray-50">
|
|
268
|
+
<Label class="mx-4 my-4 text-base text-gray-700" text="Main content" />
|
|
726
269
|
</View>
|
|
727
270
|
</View>
|
|
728
271
|
```
|
|
729
272
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
```xml
|
|
733
|
-
<!-- Half width -->
|
|
734
|
-
<View class="w-(50%)">...</View>
|
|
735
|
-
|
|
736
|
-
<!-- One-third width -->
|
|
737
|
-
<View class="w-(33.33%)">...</View>
|
|
738
|
-
|
|
739
|
-
<!-- Custom percentage -->
|
|
740
|
-
<View class="w-(75%)">...</View>
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
---
|
|
744
|
-
|
|
745
|
-
## 3. Typography
|
|
746
|
-
|
|
747
|
-
### Font Scales
|
|
748
|
-
|
|
749
|
-
PurgeTSS provides consistent font size utilities:
|
|
750
|
-
|
|
751
|
-
| Class | Size | Usage |
|
|
752
|
-
| ----------- | ---- | ------------------- |
|
|
753
|
-
| `text-xs` | 12px | Captions, labels |
|
|
754
|
-
| `text-sm` | 14px | Secondary text |
|
|
755
|
-
| `text-base` | 16px | Body text (default) |
|
|
756
|
-
| `text-lg` | 18px | Subheadings |
|
|
757
|
-
| `text-xl` | 20px | Headings |
|
|
758
|
-
| `text-2xl` | 24px | Large headings |
|
|
759
|
-
| `text-3xl` | 30px | Display headings |
|
|
760
|
-
| `text-4xl` | 36px | Hero text |
|
|
761
|
-
|
|
762
|
-
#### Typography Examples
|
|
763
|
-
|
|
764
|
-
```xml
|
|
765
|
-
<!-- Display heading -->
|
|
766
|
-
<Label class="text-4xl font-bold text-gray-900" text="Hero Title" />
|
|
767
|
-
|
|
768
|
-
<!-- Page heading -->
|
|
769
|
-
<Label class="text-2xl font-bold text-gray-800" text="Page Title" />
|
|
770
|
-
|
|
771
|
-
<!-- Section heading -->
|
|
772
|
-
<Label class="text-xl font-semibold text-gray-800" text="Section Title" />
|
|
773
|
-
|
|
774
|
-
<!-- Body text -->
|
|
775
|
-
<Label class="text-base text-gray-600" text="Regular body text content" />
|
|
776
|
-
|
|
777
|
-
<!-- Caption -->
|
|
778
|
-
<Label class="text-xs text-gray-500" text="Caption text" />
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
### Font Weights
|
|
782
|
-
|
|
783
|
-
```xml
|
|
784
|
-
<Label class="font-light" text="Light weight (300)" />
|
|
785
|
-
<Label class="font-normal" text="Normal weight (400)" />
|
|
786
|
-
<Label class="font-medium" text="Medium weight (500)" />
|
|
787
|
-
<Label class="font-semibold" text="Semibold weight (600)" />
|
|
788
|
-
<Label class="font-bold" text="Bold weight (700)" />
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
### Text Alignment and Truncation
|
|
792
|
-
|
|
793
|
-
#### Text Alignment
|
|
794
|
-
|
|
795
|
-
```xml
|
|
796
|
-
<Label class="text-left" text="Left aligned" />
|
|
797
|
-
<Label class="text-center" text="Center aligned" />
|
|
798
|
-
<Label class="text-right" text="Right aligned" />
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
#### Text Truncation
|
|
273
|
+
### Percentage Layout
|
|
802
274
|
|
|
803
275
|
```xml
|
|
804
|
-
|
|
805
|
-
<
|
|
806
|
-
|
|
807
|
-
<!-- Multi-line truncation -->
|
|
808
|
-
<Label class="line-clamp-2" text="Long text that will truncate after 2 lines with ellipsis..." />
|
|
809
|
-
<Label class="line-clamp-3" text="Long text that will truncate after 3 lines with ellipsis..." />
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
#### Text Transformations
|
|
813
|
-
|
|
814
|
-
```xml
|
|
815
|
-
<Label class="uppercase" text="uppercase text" />
|
|
816
|
-
<Label class="lowercase" text="LOWERCASE TEXT" />
|
|
817
|
-
<Label class="capitalize" text="capitalize each word" />
|
|
818
|
-
```
|
|
819
|
-
|
|
820
|
-
---
|
|
821
|
-
|
|
822
|
-
## 4. Colors and Themes
|
|
823
|
-
|
|
824
|
-
### Semantic Color Naming
|
|
825
|
-
|
|
826
|
-
Use semantic color names for better maintainability:
|
|
827
|
-
|
|
828
|
-
#### Primary Colors
|
|
829
|
-
|
|
830
|
-
```xml
|
|
831
|
-
<!-- Brand colors -->
|
|
832
|
-
<View class="bg-brand-500">...</View>
|
|
833
|
-
<View class="bg-brand-600">...</View>
|
|
834
|
-
<View class="bg-brand-700">...</View>
|
|
835
|
-
|
|
836
|
-
<!-- Gray scale -->
|
|
837
|
-
<View class="bg-gray-50">...</View>
|
|
838
|
-
<View class="bg-gray-100">...</View>
|
|
839
|
-
<View class="bg-gray-200">...</View>
|
|
840
|
-
<View class="bg-gray-300">...</View>
|
|
841
|
-
<View class="bg-gray-400">...</View>
|
|
842
|
-
<View class="bg-gray-500">...</View>
|
|
843
|
-
<View class="bg-gray-600">...</View>
|
|
844
|
-
<View class="bg-gray-700">...</View>
|
|
845
|
-
<View class="bg-gray-800">...</View>
|
|
846
|
-
<View class="bg-gray-900">...</View>
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
#### Semantic Colors
|
|
850
|
-
|
|
851
|
-
```xml
|
|
852
|
-
<!-- Success -->
|
|
853
|
-
<View class="bg-green-500">...</View>
|
|
854
|
-
<Label class="text-green-500" text="Success message" />
|
|
855
|
-
|
|
856
|
-
<!-- Warning -->
|
|
857
|
-
<View class="bg-yellow-500">...</View>
|
|
858
|
-
<Label class="text-yellow-500" text="Warning message" />
|
|
859
|
-
|
|
860
|
-
<!-- Error -->
|
|
861
|
-
<View class="bg-red-500">...</View>
|
|
862
|
-
<Label class="text-red-500" text="Error message" />
|
|
863
|
-
|
|
864
|
-
<!-- Info -->
|
|
865
|
-
<View class="bg-blue-500">...</View>
|
|
866
|
-
<Label class="text-blue-500" text="Info message" />
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
### Dark Mode Considerations
|
|
870
|
-
|
|
871
|
-
Use semantic color names that adapt to theme:
|
|
872
|
-
|
|
873
|
-
```xml
|
|
874
|
-
<!-- Adaptive background -->
|
|
875
|
-
<View class="bg-white dark:bg-gray-900">...</View>
|
|
876
|
-
|
|
877
|
-
<!-- Adaptive text -->
|
|
878
|
-
<Label class="text-gray-800 dark:text-gray-100" text="Adaptive text" />
|
|
879
|
-
|
|
880
|
-
<!-- Adaptive borders -->
|
|
881
|
-
<View class="border-gray-200 dark:border-gray-700">...</View>
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
### Platform-Specific Colors
|
|
885
|
-
|
|
886
|
-
#### iOS System Colors
|
|
887
|
-
|
|
888
|
-
```xml
|
|
889
|
-
<!-- iOS blue -->
|
|
890
|
-
<Button class="bg-ios-blue text-white" title="iOS Button" />
|
|
891
|
-
|
|
892
|
-
<!-- iOS system background -->
|
|
893
|
-
<Window class="bg-ios-system-background">
|
|
894
|
-
<View class="bg-ios-secondary-system-background">
|
|
895
|
-
<Label class="text-ios-label" text="iOS Text" />
|
|
276
|
+
<View class="horizontal w-screen">
|
|
277
|
+
<View class="w-(48%) mr-2 rounded-lg bg-blue-100">
|
|
278
|
+
<Label class="mx-4 my-4 text-blue-700" text="Left panel" />
|
|
896
279
|
</View>
|
|
897
|
-
</Window>
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
#### Material Design Colors
|
|
901
|
-
|
|
902
|
-
```xml
|
|
903
|
-
<!-- Material primary -->
|
|
904
|
-
<Button class="bg-material-primary text-white" title="Material Button" />
|
|
905
280
|
|
|
906
|
-
|
|
907
|
-
<
|
|
908
|
-
|
|
281
|
+
<View class="w-(48%) ml-2 rounded-lg bg-green-100">
|
|
282
|
+
<Label class="mx-4 my-4 text-green-700" text="Right panel" />
|
|
283
|
+
</View>
|
|
909
284
|
</View>
|
|
910
285
|
```
|
|
911
286
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
## 5. Icons
|
|
915
|
-
|
|
916
|
-
### Font Awesome 7 Icon Fonts
|
|
917
|
-
|
|
918
|
-
Font Awesome 7 uses style prefixes for different icon sets:
|
|
919
|
-
|
|
920
|
-
```xml
|
|
921
|
-
<!-- Solid icons (free) -->
|
|
922
|
-
<Label class="fas fa-home" />
|
|
923
|
-
<Label class="fas fa-user" />
|
|
924
|
-
<Label class="fas fa-envelope" />
|
|
925
|
-
|
|
926
|
-
<!-- Regular icons (free) -->
|
|
927
|
-
<Label class="far fa-heart" />
|
|
928
|
-
<Label class="far fa-star" />
|
|
929
|
-
<Label class="far fa-bookmark" />
|
|
930
|
-
|
|
931
|
-
<!-- Brand icons (free) -->
|
|
932
|
-
<Label class="fab fa-twitter" />
|
|
933
|
-
<Label class="fab fa-facebook" />
|
|
934
|
-
<Label class="fab fa-github" />
|
|
935
|
-
|
|
936
|
-
<!-- Pro-only styles (require Font Awesome Pro) -->
|
|
937
|
-
<Label class="fal fa-camera" /> <!-- Light -->
|
|
938
|
-
<Label class="fass fa-star" /> <!-- Sharp Solid -->
|
|
939
|
-
```
|
|
940
|
-
|
|
941
|
-
### Icon Sizing and Alignment
|
|
287
|
+
## Typography and Icons
|
|
942
288
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
```xml
|
|
946
|
-
<Label class="fas fa-home text-xs" /> <!-- 12px -->
|
|
947
|
-
<Label class="fas fa-home text-sm" /> <!-- 14px -->
|
|
948
|
-
<Label class="fas fa-home text-base" /> <!-- 16px -->
|
|
949
|
-
<Label class="fas fa-home text-lg" /> <!-- 18px -->
|
|
950
|
-
<Label class="fas fa-home text-xl" /> <!-- 20px -->
|
|
951
|
-
<Label class="fas fa-home text-2xl" /> <!-- 24px -->
|
|
952
|
-
<Label class="fas fa-home text-3xl" /> <!-- 30px -->
|
|
953
|
-
<Label class="fas fa-home text-4xl" /> <!-- 36px -->
|
|
954
|
-
```
|
|
955
|
-
|
|
956
|
-
#### Icon Colors
|
|
957
|
-
|
|
958
|
-
```xml
|
|
959
|
-
<Label class="fas fa-home text-gray-500" />
|
|
960
|
-
<Label class="fas fa-home text-brand-500" />
|
|
961
|
-
<Label class="fas fa-home text-(#FF5733)" /> <!-- Arbitrary color -->
|
|
962
|
-
```
|
|
289
|
+
### Typography Scale
|
|
963
290
|
|
|
964
|
-
|
|
291
|
+
- `text-xs`: 12px
|
|
292
|
+
- `text-sm`: 14px
|
|
293
|
+
- `text-base`: 16px
|
|
294
|
+
- `text-lg`: 18px
|
|
295
|
+
- `text-xl`: 20px
|
|
296
|
+
- `text-2xl`: 24px
|
|
297
|
+
- `text-3xl`: 30px
|
|
965
298
|
|
|
966
|
-
|
|
299
|
+
### Icon Pattern
|
|
967
300
|
|
|
968
301
|
```xml
|
|
969
302
|
<View class="horizontal">
|
|
@@ -972,905 +305,41 @@ Font Awesome 7 uses style prefixes for different icon sets:
|
|
|
972
305
|
</View>
|
|
973
306
|
```
|
|
974
307
|
|
|
975
|
-
|
|
308
|
+
## Accessibility
|
|
976
309
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
<!-- ALTERNATIVE: View wrapper (more verbose) -->
|
|
982
|
-
<View class="bg-brand-500 h-12 w-12 rounded-full">
|
|
983
|
-
<Label class="center fas fa-plus text-xl text-white" />
|
|
984
|
-
</View>
|
|
985
|
-
```
|
|
310
|
+
- Keep primary touch targets around `44x44` or larger.
|
|
311
|
+
- Pair icons with visible labels or `accessibilityLabel` values.
|
|
312
|
+
- Preserve contrast between text and background.
|
|
313
|
+
- Avoid using color alone for validation or state.
|
|
986
314
|
|
|
987
|
-
|
|
315
|
+
Example:
|
|
988
316
|
|
|
989
317
|
```xml
|
|
990
|
-
|
|
991
|
-
<View class="horizontal mx-3 mt-3 w-auto">
|
|
992
|
-
<!-- Icon buttons with horizontal spacing -->
|
|
993
|
-
<Label id="backButton" class="rounded-full-12 fa-solid fa-arrow-left mx-1 bg-white/20 text-center text-xl text-white" />
|
|
994
|
-
<Label id="shareButton" class="rounded-full-12 fa-solid fa-share-nodes mx-1 bg-white/20 text-center text-xl text-white" />
|
|
995
|
-
<Label id="favoriteButton" class="rounded-full-12 fa-solid fa-heart mx-1 bg-white/20 text-center text-xl text-white" />
|
|
996
|
-
</View>
|
|
997
|
-
|
|
998
|
-
<!-- To shift all items to one side, add ml-xx or mr-xx to the container -->
|
|
999
|
-
<View class="horizontal ml-0 mr-3 mt-3 w-auto">
|
|
1000
|
-
<Label class="rounded-full-12 fa-solid fa-arrow-left ... mx-1 bg-white/20" />
|
|
1001
|
-
<Label class="rounded-full-12 fa-solid fa-share-nodes ... mx-1 bg-white/20" />
|
|
1002
|
-
</View>
|
|
318
|
+
<Button class="h-12 rounded-lg bg-blue-500 text-white" title="Continue" accessibilityLabel="Continue to next step" />
|
|
1003
319
|
```
|
|
1004
320
|
|
|
1005
|
-
|
|
1006
|
-
- `horizontal w-auto` - Container takes width of children, centers them in available space
|
|
1007
|
-
- `mx-1` on each child - Consistent horizontal spacing between icons
|
|
1008
|
-
- `ml-0`/`mr-3` on container - Shifts the entire group to left/right edge
|
|
1009
|
-
|
|
1010
|
-
#### Icon Badge
|
|
1011
|
-
|
|
1012
|
-
```xml
|
|
1013
|
-
<View class="relative">
|
|
1014
|
-
<Label class="fas fa-bell text-xl" />
|
|
1015
|
-
<View class="absolute -right-1 -top-1 h-4 w-4 rounded-full bg-red-500">
|
|
1016
|
-
<Label class="center text-xs font-bold text-white" text="3" />
|
|
1017
|
-
</View>
|
|
1018
|
-
</View>
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
#### Icon Group
|
|
1022
|
-
|
|
1023
|
-
```xml
|
|
1024
|
-
<View class="horizontal">
|
|
1025
|
-
<Label class="fab fa-twitter mr-4 text-2xl text-blue-400" />
|
|
1026
|
-
<Label class="fab fa-facebook mr-4 text-2xl text-blue-600" />
|
|
1027
|
-
<Label class="fab fa-instagram mr-4 text-2xl text-pink-500" />
|
|
1028
|
-
<Label class="fab fa-github text-2xl text-gray-800" />
|
|
1029
|
-
</View>
|
|
1030
|
-
```
|
|
1031
|
-
|
|
1032
|
-
---
|
|
1033
|
-
|
|
1034
|
-
## 6. Accessibility
|
|
1035
|
-
|
|
1036
|
-
### Accessibility Labels
|
|
1037
|
-
|
|
1038
|
-
All interactive elements should have accessibility labels:
|
|
1039
|
-
|
|
1040
|
-
```xml
|
|
1041
|
-
<!-- Button with label -->
|
|
1042
|
-
<Button class="bg-brand-500 text-white" title="Save" accessibilityLabel="Save changes" />
|
|
1043
|
-
|
|
1044
|
-
<!-- Icon button with label (simplified) -->
|
|
1045
|
-
<Label class="rounded-full-12 bg-brand-500 fa-solid fa-plus text-center text-white" accessibilityLabel="Add new item" />
|
|
1046
|
-
|
|
1047
|
-
<!-- Image with label -->
|
|
1048
|
-
<ImageView image="/images/avatar.jpg" accessibilityLabel="User avatar" />
|
|
1049
|
-
```
|
|
1050
|
-
|
|
1051
|
-
### Touch Target Sizes
|
|
1052
|
-
|
|
1053
|
-
Ensure touch targets are at least 44dp for iOS and 48dp for Android:
|
|
1054
|
-
|
|
1055
|
-
```xml
|
|
1056
|
-
<!-- Minimum touch target (44dp) -->
|
|
1057
|
-
<Button class="min-w-44 min-h-44 bg-brand-500" title="Button" />
|
|
1058
|
-
|
|
1059
|
-
<!-- Icon button with proper touch target (simplified) -->
|
|
1060
|
-
<Label class="rounded-full-12 fa-solid fa-cog text-center text-xl" accessibilityLabel="Settings" />
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
### Color Contrast
|
|
1064
|
-
|
|
1065
|
-
Ensure sufficient color contrast for text:
|
|
1066
|
-
|
|
1067
|
-
```xml
|
|
1068
|
-
<!-- Good contrast examples -->
|
|
1069
|
-
<View class="bg-white">
|
|
1070
|
-
<Label class="text-gray-900" text="Dark text on white background" />
|
|
1071
|
-
</View>
|
|
1072
|
-
|
|
1073
|
-
<View class="bg-gray-900">
|
|
1074
|
-
<Label class="text-white" text="White text on dark background" />
|
|
1075
|
-
</View>
|
|
1076
|
-
|
|
1077
|
-
<View class="bg-brand-500">
|
|
1078
|
-
<Label class="font-semibold text-white" text="White text on brand color" />
|
|
1079
|
-
</View>
|
|
1080
|
-
```
|
|
1081
|
-
|
|
1082
|
-
### Screen Reader Support
|
|
1083
|
-
|
|
1084
|
-
```xml
|
|
1085
|
-
<!-- Hide decorative elements -->
|
|
1086
|
-
<View class="absolute right-0 top-0" accessibilityHidden="true">
|
|
1087
|
-
<Label class="fas fa-star text-yellow-500" />
|
|
1088
|
-
</View>
|
|
1089
|
-
|
|
1090
|
-
<!-- Provide hints for complex controls -->
|
|
1091
|
-
<Slider accessibilityLabel="Volume" accessibilityHint="Adjust the volume level" accessibilityValue="50 percent" />
|
|
1092
|
-
|
|
1093
|
-
<!-- State changes -->
|
|
1094
|
-
<Switch id="notificationSwitch" accessibilityLabel="Enable notifications" />
|
|
1095
|
-
```
|
|
1096
|
-
|
|
1097
|
-
```javascript
|
|
1098
|
-
// Update accessibility value on state change
|
|
1099
|
-
$.notificationSwitch.addEventListener('change', (e) => {
|
|
1100
|
-
$.notificationSwitch.accessibilityValue = e.value ? 'On' : 'Off'
|
|
1101
|
-
})
|
|
1102
|
-
```
|
|
1103
|
-
|
|
1104
|
-
---
|
|
1105
|
-
|
|
1106
|
-
## 7. Performance Best Practices
|
|
1107
|
-
|
|
1108
|
-
### ListView Performance
|
|
1109
|
-
|
|
1110
|
-
**CRITICAL**: Never use `Ti.UI.SIZE` in ListView templates - causes jerky scrolling.
|
|
1111
|
-
|
|
1112
|
-
```xml
|
|
1113
|
-
<!-- BAD - Causes performance issues -->
|
|
1114
|
-
<ItemTemplate name="bad">
|
|
1115
|
-
<Label bindId="title" height="Ti.UI.SIZE" />
|
|
1116
|
-
</ItemTemplate>
|
|
1117
|
-
|
|
1118
|
-
<!-- GOOD - Use fixed heights -->
|
|
1119
|
-
<ItemTemplate name="good">
|
|
1120
|
-
<Label bindId="title" height="40" />
|
|
1121
|
-
</ItemTemplate>
|
|
1122
|
-
```
|
|
1123
|
-
|
|
1124
|
-
### View Recycling
|
|
1125
|
-
|
|
1126
|
-
ListViews recycle views. Always update via data, not direct access:
|
|
1127
|
-
|
|
1128
|
-
```javascript
|
|
1129
|
-
// BAD - Direct modification
|
|
1130
|
-
function handleClick(e) {
|
|
1131
|
-
e.source.text = "Updated"
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
// GOOD - Update via data
|
|
1135
|
-
function handleClick(e) {
|
|
1136
|
-
const item = e.section.getItemAt(e.itemIndex)
|
|
1137
|
-
item.title.text = "Updated"
|
|
1138
|
-
e.section.updateItemAt(e.itemIndex, item)
|
|
1139
|
-
}
|
|
1140
|
-
```
|
|
1141
|
-
|
|
1142
|
-
### Batch Updates
|
|
1143
|
-
|
|
1144
|
-
Use `applyProperties` to reduce bridge crossings:
|
|
1145
|
-
|
|
1146
|
-
```javascript
|
|
1147
|
-
// BAD - Multiple property updates
|
|
1148
|
-
view.backgroundColor = 'red'
|
|
1149
|
-
view.width = 100
|
|
1150
|
-
view.height = 50
|
|
1151
|
-
|
|
1152
|
-
// GOOD - Single batch update
|
|
1153
|
-
view.applyProperties({
|
|
1154
|
-
backgroundColor: 'red',
|
|
1155
|
-
width: 100,
|
|
1156
|
-
height: 50
|
|
1157
|
-
})
|
|
1158
|
-
```
|
|
1159
|
-
|
|
1160
|
-
### Minimize Template Count
|
|
1161
|
-
|
|
1162
|
-
Fewer templates = better native cell reuse:
|
|
1163
|
-
|
|
1164
|
-
```xml
|
|
1165
|
-
<!-- GOOD - One flexible template -->
|
|
1166
|
-
<ItemTemplate name="flexible">
|
|
1167
|
-
<ImageView bindId="icon" />
|
|
1168
|
-
<Label bindId="title" />
|
|
1169
|
-
<Label bindId="subtitle" /> <!-- Hidden if not needed -->
|
|
1170
|
-
</ItemTemplate>
|
|
1171
|
-
|
|
1172
|
-
<!-- BAD - Multiple similar templates -->
|
|
1173
|
-
<ItemTemplate name="withIcon">...</ItemTemplate>
|
|
1174
|
-
<ItemTemplate name="withoutIcon">...</ItemTemplate>
|
|
1175
|
-
<ItemTemplate name="withSubtitle">...</ItemTemplate>
|
|
1176
|
-
```
|
|
1177
|
-
|
|
1178
|
-
---
|
|
1179
|
-
|
|
1180
|
-
## 8. Platform-Specific Patterns
|
|
1181
|
-
|
|
1182
|
-
### iOS Patterns
|
|
1183
|
-
|
|
1184
|
-
#### Navigation Window
|
|
1185
|
-
|
|
1186
|
-
```xml
|
|
1187
|
-
<NavigationWindow id="navWindow">
|
|
1188
|
-
<Window class="bg-white">
|
|
1189
|
-
<View>
|
|
1190
|
-
<Label class="text-lg font-semibold" text="Main Screen" />
|
|
1191
|
-
<Button id="pushBtn" class="mt-4" title="Push Next Screen" />
|
|
1192
|
-
</View>
|
|
1193
|
-
</Window>
|
|
1194
|
-
</NavigationWindow>
|
|
1195
|
-
```
|
|
1196
|
-
|
|
1197
|
-
```javascript
|
|
1198
|
-
// Push new window
|
|
1199
|
-
$.pushBtn.addEventListener('click', () => {
|
|
1200
|
-
const nextWin = Ti.UI.createWindow({
|
|
1201
|
-
title: 'Next Screen',
|
|
1202
|
-
backgroundColor: 'white'
|
|
1203
|
-
})
|
|
1204
|
-
$.navWindow.openWindow(nextWin, { animated: true })
|
|
1205
|
-
})
|
|
1206
|
-
```
|
|
1207
|
-
|
|
1208
|
-
#### Tab Bar
|
|
1209
|
-
|
|
1210
|
-
```xml
|
|
1211
|
-
<TabGroup id="tabGroup">
|
|
1212
|
-
<Tab id="homeTab" icon="fa-home.png" title="Home">
|
|
1213
|
-
<Window class="bg-white">
|
|
1214
|
-
<ScrollView class="h-full w-full">
|
|
1215
|
-
<Label class="text-lg font-semibold" text="Home Screen" />
|
|
1216
|
-
</ScrollView>
|
|
1217
|
-
</Window>
|
|
1218
|
-
</Tab>
|
|
1219
|
-
|
|
1220
|
-
<Tab id="searchTab" icon="fa-search.png" title="Search">
|
|
1221
|
-
<Window class="bg-white">
|
|
1222
|
-
<ScrollView class="h-full w-full">
|
|
1223
|
-
<Label class="text-lg font-semibold" text="Search Screen" />
|
|
1224
|
-
</ScrollView>
|
|
1225
|
-
</Window>
|
|
1226
|
-
</Tab>
|
|
1227
|
-
|
|
1228
|
-
<Tab id="profileTab" icon="fa-user.png" title="Profile">
|
|
1229
|
-
<Window class="bg-white">
|
|
1230
|
-
<ScrollView class="h-full w-full">
|
|
1231
|
-
<Label class="text-lg font-semibold" text="Profile Screen" />
|
|
1232
|
-
</ScrollView>
|
|
1233
|
-
</Window>
|
|
1234
|
-
</Tab>
|
|
1235
|
-
</TabGroup>
|
|
1236
|
-
```
|
|
1237
|
-
|
|
1238
|
-
#### iOS Swipe Actions
|
|
1239
|
-
|
|
1240
|
-
```javascript
|
|
1241
|
-
const section = $.myList.sections[0]
|
|
1242
|
-
|
|
1243
|
-
// Define edit actions
|
|
1244
|
-
const deleteAction = Ti.UI.iOS.createListViewDeleteOptions({
|
|
1245
|
-
title: 'Delete'
|
|
1246
|
-
})
|
|
1247
|
-
|
|
1248
|
-
const moreAction = Ti.UI.iOS.createListViewEditAction({
|
|
1249
|
-
title: 'More',
|
|
1250
|
-
backgroundColor: 'blue',
|
|
1251
|
-
style: Ti.UI.iOS.LIST_VIEW_EDIT_ACTION_STYLE_NORMAL
|
|
1252
|
-
})
|
|
1253
|
-
|
|
1254
|
-
// Enable editing
|
|
1255
|
-
section.editActions = [deleteAction, moreAction]
|
|
1256
|
-
section.canEdit = true
|
|
1257
|
-
|
|
1258
|
-
// Handle actions
|
|
1259
|
-
$.myList.addEventListener('editaction', (e) => {
|
|
1260
|
-
if (e.action === deleteAction) {
|
|
1261
|
-
section.deleteItemsAt(e.itemIndex, 1)
|
|
1262
|
-
} else if (e.action === moreAction) {
|
|
1263
|
-
// Handle more action
|
|
1264
|
-
}
|
|
1265
|
-
})
|
|
1266
|
-
```
|
|
1267
|
-
|
|
1268
|
-
### Android Patterns
|
|
1269
|
-
|
|
1270
|
-
#### Action Bar
|
|
1271
|
-
|
|
1272
|
-
```javascript
|
|
1273
|
-
const activity = Ti.Android.currentActivity
|
|
1274
|
-
|
|
1275
|
-
activity.onCreateOptionsMenu = (e) => {
|
|
1276
|
-
const menu = e.menu
|
|
1277
|
-
|
|
1278
|
-
const searchItem = menu.add({
|
|
1279
|
-
title: 'Search',
|
|
1280
|
-
icon: Ti.Android.R.drawable.ic_menu_search,
|
|
1281
|
-
showAsAction: Ti.Android.SHOW_AS_ACTION_IF_ROOM
|
|
1282
|
-
})
|
|
1283
|
-
|
|
1284
|
-
const settingsItem = menu.add({
|
|
1285
|
-
title: 'Settings',
|
|
1286
|
-
icon: Ti.Android.R.drawable.ic_menu_preferences,
|
|
1287
|
-
showAsAction: Ti.Android.SHOW_AS_ACTION_IF_ROOM
|
|
1288
|
-
})
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
activity.onOptionsItemSelected = (e) => {
|
|
1292
|
-
switch (e.itemId) {
|
|
1293
|
-
case 0:
|
|
1294
|
-
// Handle search
|
|
1295
|
-
return true
|
|
1296
|
-
case 1:
|
|
1297
|
-
// Handle settings
|
|
1298
|
-
return true
|
|
1299
|
-
}
|
|
1300
|
-
return false
|
|
1301
|
-
}
|
|
1302
|
-
```
|
|
1303
|
-
|
|
1304
|
-
#### Back Button Handling
|
|
1305
|
-
|
|
1306
|
-
```javascript
|
|
1307
|
-
$.window.addEventListener('androidback', (e) => {
|
|
1308
|
-
e.cancelBubble = true
|
|
1309
|
-
|
|
1310
|
-
if (canGoBack()) {
|
|
1311
|
-
closeCurrentWindow()
|
|
1312
|
-
} else {
|
|
1313
|
-
showExitConfirmation()
|
|
1314
|
-
}
|
|
1315
|
-
})
|
|
1316
|
-
```
|
|
1317
|
-
|
|
1318
|
-
#### Material Design Theme
|
|
1319
|
-
|
|
1320
|
-
```xml
|
|
1321
|
-
<!-- tiapp.xml -->
|
|
1322
|
-
<android xmlns:android="http://schemas.android.com/apk/res/android">
|
|
1323
|
-
<manifest>
|
|
1324
|
-
<application android:theme="@style/Theme.Titanium.Material3.DayNight"/>
|
|
1325
|
-
</manifest>
|
|
1326
|
-
</android>
|
|
1327
|
-
```
|
|
1328
|
-
|
|
1329
|
-
#### Notifications
|
|
1330
|
-
|
|
1331
|
-
```javascript
|
|
1332
|
-
// Create notification channel (Android 8.0+)
|
|
1333
|
-
const channel = Ti.Android.createNotificationChannel({
|
|
1334
|
-
name: 'My Channel',
|
|
1335
|
-
id: 'my_channel_id',
|
|
1336
|
-
importance: Ti.Android.NOTIFICATION_IMPORTANCE_HIGH
|
|
1337
|
-
})
|
|
1338
|
-
|
|
1339
|
-
Ti.Android.NotificationManager.createNotificationChannel(channel)
|
|
1340
|
-
|
|
1341
|
-
// Create and show notification
|
|
1342
|
-
const notification = Ti.Android.createNotification({
|
|
1343
|
-
number: 1,
|
|
1344
|
-
channelId: 'my_channel_id',
|
|
1345
|
-
contentTitle: 'New Message',
|
|
1346
|
-
contentText: 'You have a new message'
|
|
1347
|
-
})
|
|
1348
|
-
|
|
1349
|
-
Ti.Android.NotificationManager.notify(1, notification)
|
|
1350
|
-
```
|
|
1351
|
-
|
|
1352
|
-
---
|
|
1353
|
-
|
|
1354
|
-
## 8. Complete UI Patterns with Controllers
|
|
1355
|
-
|
|
1356
|
-
These patterns demonstrate real-world UI implementations with both XML views and their corresponding controller JavaScript code.
|
|
1357
|
-
|
|
1358
|
-
### Loading State Pattern (Skeleton)
|
|
1359
|
-
|
|
1360
|
-
Show skeleton screens while loading data, then transition to content or error states.
|
|
1361
|
-
|
|
1362
|
-
```xml
|
|
1363
|
-
<View id="loadingState" class="wh-screen vertical">
|
|
1364
|
-
<View class="mx-4 mt-4 h-20 w-screen animate-pulse rounded-lg bg-gray-200" />
|
|
1365
|
-
<View class="mx-4 mt-4 h-4 w-8/12 animate-pulse rounded bg-gray-200" />
|
|
1366
|
-
<View class="mx-4 mt-2 h-4 w-6/12 animate-pulse rounded bg-gray-200" />
|
|
1367
|
-
</View>
|
|
1368
|
-
|
|
1369
|
-
<View id="contentState" class="wh-screen hidden">
|
|
1370
|
-
<!-- Actual content goes here -->
|
|
1371
|
-
<Label class="mx-4 my-4 text-lg font-semibold text-gray-800" text="Content loaded!" />
|
|
1372
|
-
</View>
|
|
1373
|
-
|
|
1374
|
-
<View id="errorState" class="wh-screen vertical hidden">
|
|
1375
|
-
<Label class="fa-solid fa-exclamation-circle mt-20 text-6xl text-gray-400" />
|
|
1376
|
-
<Label class="mt-4 text-lg text-gray-500" text="L('error_loading')" />
|
|
1377
|
-
<Button class="bg-primary mt-4 h-10 rounded-lg px-6 text-white" title="L('retry')" onClick="loadData" />
|
|
1378
|
-
</View>
|
|
1379
|
-
```
|
|
1380
|
-
|
|
1381
|
-
```javascript
|
|
1382
|
-
// Controller
|
|
1383
|
-
function setState(state) {
|
|
1384
|
-
const states = ['loadingState', 'contentState', 'errorState']
|
|
1385
|
-
states.forEach(s => { $[s].visible = s === state })
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
async function loadData() {
|
|
1389
|
-
setState('loadingState')
|
|
1390
|
-
try {
|
|
1391
|
-
const data = await api.fetchData()
|
|
1392
|
-
renderContent(data)
|
|
1393
|
-
setState('contentState')
|
|
1394
|
-
} catch (error) {
|
|
1395
|
-
setState('errorState')
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
// Initial load
|
|
1400
|
-
loadData()
|
|
1401
|
-
```
|
|
1402
|
-
|
|
1403
|
-
### Pull-to-Refresh Pattern
|
|
1404
|
-
|
|
1405
|
-
Enable users to refresh content by pulling down on a ListView.
|
|
1406
|
-
|
|
1407
|
-
```xml
|
|
1408
|
-
<ListView id="listView" class="wh-screen">
|
|
1409
|
-
<RefreshControl id="refreshControl" onRefresh="onRefresh" />
|
|
1410
|
-
<ListSection id="section" />
|
|
1411
|
-
</ListView>
|
|
1412
|
-
```
|
|
1413
|
-
|
|
1414
|
-
```javascript
|
|
1415
|
-
// Controller
|
|
1416
|
-
let isRefreshing = false
|
|
1417
|
-
|
|
1418
|
-
async function onRefresh() {
|
|
1419
|
-
if (isRefreshing) return
|
|
1420
|
-
isRefreshing = true
|
|
1421
|
-
try {
|
|
1422
|
-
const data = await api.fetchLatest()
|
|
1423
|
-
renderItems(data)
|
|
1424
|
-
} catch (error) {
|
|
1425
|
-
showError(error.message)
|
|
1426
|
-
} finally {
|
|
1427
|
-
$.refreshControl.endRefreshing()
|
|
1428
|
-
isRefreshing = false
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
function renderItems(data) {
|
|
1433
|
-
const items = data.map(item => ({
|
|
1434
|
-
properties: { itemId: item.id },
|
|
1435
|
-
title: { text: item.name }
|
|
1436
|
-
}))
|
|
1437
|
-
$.section.setItems(items)
|
|
1438
|
-
}
|
|
1439
|
-
```
|
|
1440
|
-
|
|
1441
|
-
### SearchBar with ListView
|
|
1442
|
-
|
|
1443
|
-
Implement debounced search with real-time filtering.
|
|
1444
|
-
|
|
1445
|
-
```xml
|
|
1446
|
-
<Window class="bg-gray-50">
|
|
1447
|
-
<SearchBar id="searchBar" class="w-screen" hintText="L('search_contacts')" showCancel="true" onChange="onSearchChange" onCancel="onSearchCancel" />
|
|
1448
|
-
<ListView id="listView" class="wh-screen" top="50">
|
|
1449
|
-
<Templates>
|
|
1450
|
-
<ItemTemplate name="contact" height="64">
|
|
1451
|
-
<View class="horizontal h-16 w-screen bg-white">
|
|
1452
|
-
<Label bindId="avatar" class="fa-solid fa-user-circle ml-4 text-3xl text-gray-400" />
|
|
1453
|
-
<View class="vertical ml-3">
|
|
1454
|
-
<Label bindId="name" class="text-base font-semibold" />
|
|
1455
|
-
<Label bindId="phone" class="text-sm text-gray-500" />
|
|
1456
|
-
</View>
|
|
1457
|
-
</View>
|
|
1458
|
-
</ItemTemplate>
|
|
1459
|
-
</Templates>
|
|
1460
|
-
<ListSection id="section" />
|
|
1461
|
-
</ListView>
|
|
1462
|
-
</Window>
|
|
1463
|
-
```
|
|
1464
|
-
|
|
1465
|
-
```javascript
|
|
1466
|
-
// Controller
|
|
1467
|
-
let allContacts = []
|
|
1468
|
-
let searchTimeout = null
|
|
1469
|
-
|
|
1470
|
-
function onSearchChange(e) {
|
|
1471
|
-
clearTimeout(searchTimeout)
|
|
1472
|
-
searchTimeout = setTimeout(() => {
|
|
1473
|
-
filterContacts(e.value)
|
|
1474
|
-
}, 300)
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
function onSearchCancel() {
|
|
1478
|
-
$.searchBar.value = ''
|
|
1479
|
-
filterContacts('')
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
function filterContacts(query) {
|
|
1483
|
-
const q = query.toLowerCase().trim()
|
|
1484
|
-
const filtered = q
|
|
1485
|
-
? allContacts.filter(c => c.name.toLowerCase().includes(q) || c.phone.includes(q))
|
|
1486
|
-
: allContacts
|
|
1487
|
-
renderContacts(filtered)
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
function renderContacts(contacts) {
|
|
1491
|
-
$.section.items = contacts.map(c => ({
|
|
1492
|
-
template: 'contact',
|
|
1493
|
-
properties: { itemId: c.id },
|
|
1494
|
-
name: { text: c.name },
|
|
1495
|
-
phone: { text: c.phone }
|
|
1496
|
-
}))
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
function cleanup() {
|
|
1500
|
-
clearTimeout(searchTimeout)
|
|
1501
|
-
$.destroy()
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
$.cleanup = cleanup
|
|
1505
|
-
|
|
1506
|
-
// Load initial data
|
|
1507
|
-
async function loadContacts() {
|
|
1508
|
-
try {
|
|
1509
|
-
allContacts = await api.fetchContacts()
|
|
1510
|
-
renderContacts(allContacts)
|
|
1511
|
-
} catch (error) {
|
|
1512
|
-
Ti.UI.createAlertDialog({
|
|
1513
|
-
title: L('error'),
|
|
1514
|
-
message: error.message
|
|
1515
|
-
}).show()
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
loadContacts()
|
|
1520
|
-
```
|
|
1521
|
-
|
|
1522
|
-
### Complete CRUD with ListView
|
|
1523
|
-
|
|
1524
|
-
Full product list with create, read, update, and delete operations.
|
|
1525
|
-
|
|
1526
|
-
```xml
|
|
1527
|
-
<Window class="bg-gray-50">
|
|
1528
|
-
<ListView id="listView" class="wh-screen">
|
|
1529
|
-
<RefreshControl id="refresh" onRefresh="onRefresh" />
|
|
1530
|
-
<Templates>
|
|
1531
|
-
<ItemTemplate name="product" height="80">
|
|
1532
|
-
<View class="horizontal mb-2 h-20 w-screen bg-white">
|
|
1533
|
-
<ImageView bindId="image" class="wh-16 ml-3 rounded-lg" />
|
|
1534
|
-
<View class="vertical ml-3 w-auto">
|
|
1535
|
-
<Label bindId="name" class="text-base font-semibold" />
|
|
1536
|
-
<Label bindId="price" class="text-sm text-green-600" />
|
|
1537
|
-
<Label bindId="stock" class="text-xs text-gray-400" />
|
|
1538
|
-
</View>
|
|
1539
|
-
</View>
|
|
1540
|
-
</ItemTemplate>
|
|
1541
|
-
</Templates>
|
|
1542
|
-
<ListSection id="section" dataCollection="products">
|
|
1543
|
-
<ListItem template="product" image:image="{imageUrl}" name:text="{name}" price:text="${price}" stock:text="{stock} in stock" />
|
|
1544
|
-
</ListSection>
|
|
1545
|
-
</ListView>
|
|
1546
|
-
|
|
1547
|
-
<Button id="addBtn" class="bg-primary rounded-full-14 absolute bottom-6 right-6 shadow-lg" onClick="onAddProduct">
|
|
1548
|
-
<Label class="fa-solid fa-plus text-xl text-white" />
|
|
1549
|
-
</Button>
|
|
1550
|
-
</Window>
|
|
1551
|
-
```
|
|
1552
|
-
|
|
1553
|
-
```javascript
|
|
1554
|
-
// Controller
|
|
1555
|
-
const productService = require('/services/productService')
|
|
1556
|
-
|
|
1557
|
-
// Handle item click
|
|
1558
|
-
function onItemClick(e) {
|
|
1559
|
-
const item = e.section.getItemAt(e.itemIndex)
|
|
1560
|
-
const productId = item.properties.itemId
|
|
1561
|
-
|
|
1562
|
-
const detailWin = Alloy.createController('productDetail', { id: productId }).getView()
|
|
1563
|
-
detailWin.open()
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
// Refresh data
|
|
1567
|
-
async function onRefresh() {
|
|
1568
|
-
try {
|
|
1569
|
-
const products = await productService.fetchAll()
|
|
1570
|
-
Alloy.Collections.products.reset(products)
|
|
1571
|
-
} catch (error) {
|
|
1572
|
-
showError(error.message)
|
|
1573
|
-
} finally {
|
|
1574
|
-
$.refresh.endRefreshing()
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
// Add new product
|
|
1579
|
-
function onAddProduct() {
|
|
1580
|
-
const formWin = Alloy.createController('productForm', { mode: 'create' }).getView()
|
|
1581
|
-
formWin.open({ modal: true })
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
// Show error message
|
|
1585
|
-
function showError(message) {
|
|
1586
|
-
Ti.UI.createAlertDialog({
|
|
1587
|
-
title: L('error'),
|
|
1588
|
-
message: message
|
|
1589
|
-
}).show()
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
// Event listeners
|
|
1593
|
-
$.listView.addEventListener('itemclick', onItemClick)
|
|
1594
|
-
|
|
1595
|
-
// Cleanup
|
|
1596
|
-
function cleanup() {
|
|
1597
|
-
$.listView.removeEventListener('itemclick', onItemClick)
|
|
1598
|
-
$.destroy()
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
$.cleanup = cleanup
|
|
1602
|
-
|
|
1603
|
-
// Load initial data
|
|
1604
|
-
onRefresh()
|
|
1605
|
-
```
|
|
1606
|
-
|
|
1607
|
-
### Form with Real-Time Validation
|
|
1608
|
-
|
|
1609
|
-
Registration form with field-level validation and error feedback.
|
|
1610
|
-
|
|
1611
|
-
```xml
|
|
1612
|
-
<Window class="bg-gray-50">
|
|
1613
|
-
<ScrollView class="wh-screen">
|
|
1614
|
-
<View class="vertical">
|
|
1615
|
-
<Label class="mx-4 mb-6 mt-6 text-2xl font-bold text-gray-800" text="L('create_account')" />
|
|
1616
|
-
|
|
1617
|
-
<!-- Name Field -->
|
|
1618
|
-
<View class="mb-4">
|
|
1619
|
-
<Label id="nameLabel" class="mx-4 mb-2 text-sm font-medium text-gray-700" text="L('full_name')" />
|
|
1620
|
-
<TextField id="nameField" class="border-(1) return-key-type-next mx-4 mt-1 h-12 w-screen rounded-lg border-gray-300 px-3" hintText="L('enter_name')" onChange="validateName" />
|
|
1621
|
-
<Label id="nameError" class="mx-4 mt-1 hidden text-xs text-red-500" />
|
|
1622
|
-
</View>
|
|
1623
|
-
|
|
1624
|
-
<!-- Email Field -->
|
|
1625
|
-
<View class="mb-4">
|
|
1626
|
-
<Label id="emailLabel" class="mx-4 mb-2 text-sm font-medium text-gray-700" text="L('email')" />
|
|
1627
|
-
<TextField id="emailField" class="border-(1) return-key-type-next mx-4 mt-1 h-12 w-screen rounded-lg border-gray-300 px-3" keyboardType="Ti.UI.KEYBOARD_TYPE_EMAIL" autocapitalization="Ti.UI.TEXT_AUTOCAPITALIZATION_NONE" hintText="L('enter_email')" onChange="validateEmail" />
|
|
1628
|
-
<Label id="emailError" class="mx-4 mt-1 hidden text-xs text-red-500" />
|
|
1629
|
-
</View>
|
|
1630
|
-
|
|
1631
|
-
<!-- Password Field -->
|
|
1632
|
-
<View class="mb-4">
|
|
1633
|
-
<Label id="passwordLabel" class="mx-4 mb-2 text-sm font-medium text-gray-700" text="L('password')" />
|
|
1634
|
-
<TextField id="passwordField" class="border-(1) return-key-type-done mx-4 mt-1 h-12 w-screen rounded-lg border-gray-300 px-3" passwordMask="true" hintText="L('enter_password')" onChange="validatePassword" />
|
|
1635
|
-
<Label id="passwordError" class="mx-4 mt-1 hidden text-xs text-red-500" />
|
|
1636
|
-
</View>
|
|
1637
|
-
|
|
1638
|
-
<!-- Terms Switch -->
|
|
1639
|
-
<View class="horizontal mx-4 mb-6">
|
|
1640
|
-
<Switch id="termsSwitch" onChange="validateTerms" />
|
|
1641
|
-
<Label class="ml-3 text-sm text-gray-700" text="L('agree_to_terms')" />
|
|
1642
|
-
</View>
|
|
1643
|
-
|
|
1644
|
-
<!-- Submit Button -->
|
|
1645
|
-
<Button id="submitBtn" class="bg-primary mx-4 h-12 rounded-lg text-base font-semibold text-white" title="L('create_account')" onClick="onSubmit" enabled="false" />
|
|
1646
|
-
</View>
|
|
1647
|
-
</ScrollView>
|
|
1648
|
-
</Window>
|
|
1649
|
-
```
|
|
1650
|
-
|
|
1651
|
-
```javascript
|
|
1652
|
-
// Controller
|
|
1653
|
-
const validationState = {
|
|
1654
|
-
name: false,
|
|
1655
|
-
email: false,
|
|
1656
|
-
password: false,
|
|
1657
|
-
terms: false
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
function validateName(e) {
|
|
1661
|
-
const value = e.value.trim()
|
|
1662
|
-
const isValid = value.length >= 2
|
|
1663
|
-
|
|
1664
|
-
if (!isValid && value.length > 0) {
|
|
1665
|
-
$.nameError.text = L('name_too_short')
|
|
1666
|
-
$.nameError.visible = true
|
|
1667
|
-
$.nameField.borderColor = '#EF4444'
|
|
1668
|
-
$.nameLabel.color = '#EF4444'
|
|
1669
|
-
} else {
|
|
1670
|
-
$.nameError.visible = false
|
|
1671
|
-
$.nameField.borderColor = isValid ? '#10B981' : '#D1D5DB'
|
|
1672
|
-
$.nameLabel.color = isValid ? '#10B981' : '#374151'
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
validationState.name = isValid
|
|
1676
|
-
updateSubmitButton()
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
function validateEmail(e) {
|
|
1680
|
-
const value = e.value.trim()
|
|
1681
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
1682
|
-
const isValid = emailRegex.test(value)
|
|
1683
|
-
|
|
1684
|
-
if (!isValid && value.length > 0) {
|
|
1685
|
-
$.emailError.text = L('invalid_email')
|
|
1686
|
-
$.emailError.visible = true
|
|
1687
|
-
$.emailField.borderColor = '#EF4444'
|
|
1688
|
-
$.emailLabel.color = '#EF4444'
|
|
1689
|
-
} else {
|
|
1690
|
-
$.emailError.visible = false
|
|
1691
|
-
$.emailField.borderColor = isValid ? '#10B981' : '#D1D5DB'
|
|
1692
|
-
$.emailLabel.color = isValid ? '#10B981' : '#374151'
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
validationState.email = isValid
|
|
1696
|
-
updateSubmitButton()
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
function validatePassword(e) {
|
|
1700
|
-
const value = e.value
|
|
1701
|
-
const isValid = value.length >= 8
|
|
1702
|
-
|
|
1703
|
-
if (!isValid && value.length > 0) {
|
|
1704
|
-
$.passwordError.text = L('password_too_short')
|
|
1705
|
-
$.passwordError.visible = true
|
|
1706
|
-
$.passwordField.borderColor = '#EF4444'
|
|
1707
|
-
$.passwordLabel.color = '#EF4444'
|
|
1708
|
-
} else {
|
|
1709
|
-
$.passwordError.visible = false
|
|
1710
|
-
$.passwordField.borderColor = isValid ? '#10B981' : '#D1D5DB'
|
|
1711
|
-
$.passwordLabel.color = isValid ? '#10B981' : '#374151'
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
validationState.password = isValid
|
|
1715
|
-
updateSubmitButton()
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
function validateTerms(e) {
|
|
1719
|
-
validationState.terms = e.value
|
|
1720
|
-
updateSubmitButton()
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
function updateSubmitButton() {
|
|
1724
|
-
const allValid = Object.values(validationState).every(v => v === true)
|
|
1725
|
-
$.submitBtn.enabled = allValid
|
|
1726
|
-
$.submitBtn.opacity = allValid ? 1.0 : 0.5
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
async function onSubmit() {
|
|
1730
|
-
if (!$.submitBtn.enabled) return
|
|
1731
|
-
|
|
1732
|
-
$.submitBtn.enabled = false
|
|
1733
|
-
$.submitBtn.title = L('creating_account')
|
|
1734
|
-
|
|
1735
|
-
try {
|
|
1736
|
-
const userData = {
|
|
1737
|
-
name: $.nameField.value.trim(),
|
|
1738
|
-
email: $.emailField.value.trim(),
|
|
1739
|
-
password: $.passwordField.value
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
await api.createAccount(userData)
|
|
1743
|
-
|
|
1744
|
-
Ti.UI.createAlertDialog({
|
|
1745
|
-
title: L('success'),
|
|
1746
|
-
message: L('account_created')
|
|
1747
|
-
}).show()
|
|
1748
|
-
|
|
1749
|
-
$.getView().close()
|
|
1750
|
-
} catch (error) {
|
|
1751
|
-
Ti.UI.createAlertDialog({
|
|
1752
|
-
title: L('error'),
|
|
1753
|
-
message: error.message
|
|
1754
|
-
}).show()
|
|
1755
|
-
$.submitBtn.enabled = true
|
|
1756
|
-
$.submitBtn.title = L('create_account')
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
```
|
|
1760
|
-
|
|
1761
|
-
### TabGroup Pattern
|
|
1762
|
-
|
|
1763
|
-
Standard tab-based navigation with separate screens.
|
|
1764
|
-
|
|
1765
|
-
```xml
|
|
1766
|
-
<!-- index.xml -->
|
|
1767
|
-
<TabGroup id="tabGroup" class="tabs-bg-white active-tint-blue-500">
|
|
1768
|
-
<Tab title="L('home')" icon="/images/icons/home.png">
|
|
1769
|
-
<Require src="tabs/home" />
|
|
1770
|
-
</Tab>
|
|
1771
|
-
|
|
1772
|
-
<Tab title="L('search')" icon="/images/icons/search.png">
|
|
1773
|
-
<Require src="tabs/search" />
|
|
1774
|
-
</Tab>
|
|
1775
|
-
|
|
1776
|
-
<Tab title="L('favorites')" icon="/images/icons/heart.png">
|
|
1777
|
-
<Require src="tabs/favorites" />
|
|
1778
|
-
</Tab>
|
|
1779
|
-
|
|
1780
|
-
<Tab title="L('profile')" icon="/images/icons/user.png">
|
|
1781
|
-
<Require src="tabs/profile" />
|
|
1782
|
-
</Tab>
|
|
1783
|
-
</TabGroup>
|
|
1784
|
-
```
|
|
1785
|
-
|
|
1786
|
-
```javascript
|
|
1787
|
-
// index.js
|
|
1788
|
-
$.tabGroup.open()
|
|
1789
|
-
|
|
1790
|
-
function cleanup() {
|
|
1791
|
-
$.destroy()
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
$.cleanup = cleanup
|
|
1795
|
-
```
|
|
1796
|
-
|
|
1797
|
-
```xml
|
|
1798
|
-
<!-- tabs/home.xml -->
|
|
1799
|
-
<Window class="bg-gray-50">
|
|
1800
|
-
<ScrollView class="wh-screen">
|
|
1801
|
-
<View class="vertical">
|
|
1802
|
-
<Label class="mx-4 my-4 text-2xl font-bold text-gray-800" text="L('home')" />
|
|
1803
|
-
<!-- Home content -->
|
|
1804
|
-
</View>
|
|
1805
|
-
</ScrollView>
|
|
1806
|
-
</Window>
|
|
1807
|
-
```
|
|
1808
|
-
|
|
1809
|
-
```xml
|
|
1810
|
-
<!-- tabs/search.xml -->
|
|
1811
|
-
<Window class="bg-gray-50">
|
|
1812
|
-
<SearchBar id="searchBar" class="w-screen" hintText="L('search')" />
|
|
1813
|
-
<ListView id="results" class="wh-screen" top="50">
|
|
1814
|
-
<ListSection id="section" />
|
|
1815
|
-
</ListView>
|
|
1816
|
-
</Window>
|
|
1817
|
-
```
|
|
1818
|
-
|
|
1819
|
-
---
|
|
1820
|
-
|
|
1821
|
-
## Quick Reference Card
|
|
1822
|
-
|
|
1823
|
-
### Common PurgeTSS Classes
|
|
1824
|
-
|
|
1825
|
-
| Category | Classes | Examples |
|
|
1826
|
-
| --------------- | -------------------------------------------- | ------------------------------- |
|
|
1827
|
-
| **Spacing** | `p-{0-8}`, `m-{0-8}`, `px-{0-8}`, `py-{0-8}` | `p-4`, `mx-2`, `py-6` |
|
|
1828
|
-
| **Colors** | `bg-{color}-{shade}`, `text-{color}-{shade}` | `bg-brand-500`, `text-gray-800` |
|
|
1829
|
-
| **Typography** | `text-{xs,sm,base,lg,xl,2xl,3xl,4xl}` | `text-xl`, `text-2xl` |
|
|
1830
|
-
| **Font Weight** | `font-{light,normal,medium,semibold,bold}` | `font-semibold`, `font-bold` |
|
|
1831
|
-
| **Borders** | `border`, `border-{color}`, `rounded-{size}` | `border-gray-300`, `rounded-lg` |
|
|
1832
|
-
| **Shadows** | `shadow-{sm,md,lg,xl,2xl}` | `shadow-md`, `shadow-lg` |
|
|
1833
|
-
| **Width** | `w-{size}`, `w-{percentage}` | `w-full`, `w-(50%)` |
|
|
1834
|
-
| **Height** | `h-{size}`, `h-screen` | `h-10`, `h-screen` |
|
|
1835
|
-
| **Display** | `hidden`, `visible` | `hidden`, `visible` |
|
|
1836
|
-
| **Icons** | `fas`, `far`, `fab fa-{icon}` | `fas fa-home`, `fab fa-twitter` |
|
|
1837
|
-
|
|
1838
|
-
### Platform Modifiers
|
|
1839
|
-
|
|
1840
|
-
```xml
|
|
1841
|
-
<!-- iOS-only -->
|
|
1842
|
-
<View class="ios:p-4 ios:bg-white">...</View>
|
|
1843
|
-
|
|
1844
|
-
<!-- Android-only -->
|
|
1845
|
-
<View class="android:p-4 android:bg-gray-100">...</View>
|
|
1846
|
-
|
|
1847
|
-
<!-- Platform-specific values -->
|
|
1848
|
-
<View class="ios:bg-white android:bg-gray-100 p-4">...</View>
|
|
1849
|
-
```
|
|
1850
|
-
|
|
1851
|
-
---
|
|
1852
|
-
|
|
1853
|
-
## Best Practices Summary
|
|
321
|
+
## Performance Notes
|
|
1854
322
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
5. **Ensure minimum touch target size** of 44dp (iOS) / 48dp (Android)
|
|
1860
|
-
6. **Use semantic color names** for better theming support
|
|
1861
|
-
7. **Test on both platforms** to ensure consistent experience
|
|
1862
|
-
8. **Follow platform conventions** - iOS NavigationWindow, Android Action Bar
|
|
1863
|
-
9. **Use PurgeTSS utility classes** for consistent styling
|
|
1864
|
-
10. **Batch property updates** using `applyProperties()` for better performance
|
|
1865
|
-
11. **Padding classes do NOT work on View** - use margin on children instead
|
|
1866
|
-
12. **Always specify Font Awesome icons** - `fas fa-home`, not just `fas`
|
|
323
|
+
- Prefer `ListView` templates for long lists.
|
|
324
|
+
- Use fixed heights for high-density scrolling content when possible.
|
|
325
|
+
- Avoid manual inline styling when a class can express the same result.
|
|
326
|
+
- Keep overlays and large composited layouts simple on lower-end devices.
|
|
1867
327
|
|
|
1868
|
-
|
|
328
|
+
## Quick Reference
|
|
1869
329
|
|
|
1870
|
-
|
|
330
|
+
| Need | Preferred Pattern |
|
|
331
|
+
| --- | --- |
|
|
332
|
+
| Full-width container | `w-screen` |
|
|
333
|
+
| Stack items vertically | `vertical` |
|
|
334
|
+
| Row of actions | `horizontal` |
|
|
335
|
+
| Fill space in overlay/modal | `h-screen w-screen` |
|
|
336
|
+
| Card spacing | Margins on children |
|
|
337
|
+
| Input inner spacing | `px-*`, `py-*` on input controls |
|
|
338
|
+
| Tablet adaptation | `tablet:*`, `handheld:*` |
|
|
339
|
+
| Overlay background | `bg-(#88000000)` |
|
|
1871
340
|
|
|
1872
|
-
|
|
1873
|
-
-
|
|
1874
|
-
-
|
|
1875
|
-
-
|
|
1876
|
-
-
|
|
341
|
+
> **⚠️ Common Pitfalls**
|
|
342
|
+
> - Do not use `md:*`, `lg:*`, `focus:*`, or other web-responsive/state prefixes.
|
|
343
|
+
> - Do not assume `w-full` behaves like `Ti.UI.FILL`.
|
|
344
|
+
> - Do not put `p-*` on container views.
|
|
345
|
+
> - Do not rely on Flexbox classes such as `flex-row`, `justify-between`, or `items-center`.
|