@maccesar/titools 2.0.7 → 2.2.1
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/AGENTS-VERCEL-RESEARCH.md +172 -0
- package/README.md +408 -389
- package/agents/{ti-researcher.md → ti-pro.md} +14 -13
- package/bin/titools.js +16 -12
- package/lib/cleanup.js +95 -0
- package/lib/commands/agents.js +146 -70
- package/lib/commands/skills.js +349 -0
- package/lib/commands/uninstall.js +189 -154
- package/lib/commands/update.js +104 -21
- package/lib/config.js +26 -20
- package/lib/downloader.js +1 -1
- package/lib/installer.js +37 -97
- package/lib/platform.js +9 -5
- package/lib/prompts/checkboxCancel.js +264 -0
- package/lib/prompts/selectCancel.js +204 -0
- package/lib/symlink.js +19 -7
- package/lib/utils.js +17 -17
- package/package.json +10 -10
- package/skills/alloy-guides/SKILL.md +8 -30
- package/skills/alloy-guides/references/CLI_TASKS.md +14 -24
- package/skills/alloy-guides/references/CONCEPTS.md +5 -25
- package/skills/alloy-guides/references/CONTROLLERS.md +2 -19
- package/skills/alloy-guides/references/MODELS.md +40 -10
- package/skills/alloy-guides/references/PURGETSS.md +1 -11
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +8 -14
- package/skills/alloy-guides/references/VIEWS_STYLES.md +14 -25
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +2 -8
- package/skills/alloy-guides/references/VIEWS_XML.md +29 -40
- package/skills/alloy-guides/references/WIDGETS.md +1 -17
- package/skills/alloy-howtos/SKILL.md +1 -22
- package/skills/alloy-howtos/references/best_practices.md +0 -17
- package/skills/alloy-howtos/references/cli_reference.md +1 -23
- package/skills/alloy-howtos/references/config_files.md +86 -15
- package/skills/alloy-howtos/references/custom_tags.md +14 -13
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +3 -22
- package/skills/alloy-howtos/references/samples.md +4 -14
- package/skills/purgetss/SKILL.md +11 -37
- package/skills/purgetss/references/EXAMPLES.md +106 -17
- package/skills/purgetss/references/animation-system.md +0 -67
- package/skills/purgetss/references/apply-directive.md +0 -18
- package/skills/purgetss/references/arbitrary-values.md +0 -25
- package/skills/purgetss/references/class-index.md +0 -46
- package/skills/purgetss/references/cli-commands.md +0 -41
- package/skills/purgetss/references/configurable-properties.md +0 -55
- package/skills/purgetss/references/custom-rules.md +0 -14
- package/skills/purgetss/references/customization-deep-dive.md +0 -49
- package/skills/purgetss/references/dynamic-component-creation.md +56 -37
- package/skills/purgetss/references/grid-layout.md +0 -32
- package/skills/purgetss/references/icon-fonts.md +0 -50
- package/skills/purgetss/references/installation-setup.md +61 -32
- package/skills/purgetss/references/migration-guide.md +127 -0
- package/skills/purgetss/references/opacity-modifier.md +0 -34
- package/skills/purgetss/references/performance-tips.md +261 -0
- package/skills/purgetss/references/platform-modifiers.md +0 -46
- package/skills/purgetss/references/smart-mappings.md +0 -12
- package/skills/purgetss/references/tikit-components.md +393 -0
- package/skills/purgetss/references/titanium-resets.md +1 -27
- package/skills/purgetss/references/ui-ux-design.md +467 -117
- package/skills/{alloy-expert → ti-expert}/SKILL.md +45 -54
- package/skills/ti-expert/references/alloy-builtins.md +387 -0
- package/skills/{alloy-expert → ti-expert}/references/alloy-structure.md +147 -40
- package/skills/ti-expert/references/anti-patterns.md +90 -0
- package/skills/ti-expert/references/cli-expert.md +109 -0
- package/skills/{alloy-expert → ti-expert}/references/code-conventions.md +182 -49
- package/skills/{alloy-expert → ti-expert}/references/controller-patterns.md +103 -93
- package/skills/{alloy-expert → ti-expert}/references/error-handling.md +12 -12
- package/skills/{alloy-expert → ti-expert}/references/examples.md +144 -85
- package/skills/{alloy-expert → ti-expert}/references/migration-patterns.md +38 -36
- package/skills/{alloy-expert → ti-expert}/references/patterns.md +8 -8
- package/skills/ti-expert/references/performance-listview.md +251 -0
- package/skills/{alloy-expert/references/performance-patterns.md → ti-expert/references/performance-optimization.md} +67 -283
- package/skills/{alloy-expert/references/security-patterns.md → ti-expert/references/security-device.md} +12 -295
- package/skills/ti-expert/references/security-fundamentals.md +284 -0
- package/skills/{alloy-expert → ti-expert}/references/state-management.md +10 -10
- package/skills/ti-expert/references/testing-e2e-ci.md +432 -0
- package/skills/ti-expert/references/testing-unit.md +433 -0
- package/skills/ti-expert/references/theming.md +394 -0
- package/skills/ti-guides/SKILL.md +1 -14
- package/skills/ti-guides/references/advanced-data-and-images.md +126 -10
- package/skills/ti-guides/references/app-distribution.md +48 -10
- package/skills/ti-guides/references/application-frameworks.md +9 -2
- package/skills/ti-guides/references/cli-reference.md +338 -82
- package/skills/ti-guides/references/coding-best-practices.md +14 -4
- package/skills/ti-guides/references/commonjs-advanced.md +103 -6
- package/skills/ti-guides/references/hello-world.md +5 -2
- package/skills/ti-guides/references/hyperloop-native-access.md +439 -46
- package/skills/ti-guides/references/javascript-primer.md +4 -3
- package/skills/ti-guides/references/resources.md +0 -1
- package/skills/ti-guides/references/style-and-conventions.md +57 -9
- package/skills/ti-guides/references/tiapp-config.md +48 -6
- package/skills/ti-howtos/SKILL.md +2 -27
- package/skills/ti-howtos/references/android-platform-deep-dives.md +41 -90
- package/skills/ti-howtos/references/automation-fastlane-appium.md +15 -14
- package/skills/ti-howtos/references/buffer-codec-streams.md +22 -22
- package/skills/ti-howtos/references/cross-platform-development.md +68 -37
- package/skills/ti-howtos/references/debugging-profiling.md +8 -53
- package/skills/ti-howtos/references/extending-titanium.md +12 -51
- package/skills/ti-howtos/references/google-maps-v2.md +30 -29
- package/skills/ti-howtos/references/ios-map-kit.md +19 -25
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +95 -283
- package/skills/ti-howtos/references/local-data-sources.md +45 -37
- package/skills/ti-howtos/references/location-and-maps.md +47 -28
- package/skills/ti-howtos/references/media-apis.md +84 -27
- package/skills/ti-howtos/references/notification-services.md +40 -89
- package/skills/ti-howtos/references/remote-data-sources.md +21 -36
- package/skills/ti-howtos/references/tutorials.md +79 -39
- package/skills/ti-howtos/references/using-modules.md +4 -30
- package/skills/ti-howtos/references/web-content-integration.md +28 -45
- package/skills/ti-howtos/references/webpack-build-pipeline.md +54 -18
- package/skills/ti-ui/SKILL.md +1 -21
- package/skills/ti-ui/references/accessibility-deep-dive.md +71 -25
- package/skills/ti-ui/references/animation-and-matrices.md +0 -47
- package/skills/ti-ui/references/application-structures.md +0 -46
- package/skills/ti-ui/references/custom-fonts-styling.md +9 -57
- package/skills/ti-ui/references/event-handling.md +32 -40
- package/skills/ti-ui/references/gestures.md +0 -40
- package/skills/ti-ui/references/icons-and-splash-screens.md +21 -44
- package/skills/ti-ui/references/layouts-and-positioning.md +47 -39
- package/skills/ti-ui/references/listviews-and-performance.md +187 -73
- package/skills/ti-ui/references/orientation.md +57 -71
- package/skills/ti-ui/references/platform-ui-android.md +114 -48
- package/skills/ti-ui/references/platform-ui-ios.md +63 -38
- package/skills/ti-ui/references/scrolling-views.md +14 -23
- package/skills/ti-ui/references/tableviews.md +6 -56
- package/AGENTS-TEMPLATE.md +0 -174
- package/lib/commands/install.js +0 -188
- package/skills/alloy-expert/references/anti-patterns.md +0 -133
- package/skills/alloy-expert/references/testing.md +0 -872
- package/skills/ti-guides/references/alloy-cli-advanced.md +0 -84
- package/skills/ti-guides/references/alloy-data-mastery.md +0 -29
- package/skills/ti-guides/references/alloy-widgets-and-themes.md +0 -19
- /package/skills/{alloy-expert → ti-expert}/assets/ControllerAutoCleanup.js +0 -0
- /package/skills/{alloy-expert → ti-expert}/references/contracts.md +0 -0
|
@@ -2,123 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive guide to building beautiful, accessible, and performant mobile UIs using Titanium SDK and PurgeTSS utility classes.
|
|
4
4
|
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [UI/UX Design Patterns for Titanium SDK with PurgeTSS](#uiux-design-patterns-for-titanium-sdk-with-purgetss)
|
|
8
|
-
- [Table of Contents](#table-of-contents)
|
|
9
|
-
- [1. Common UI Components](#1-common-ui-components)
|
|
10
|
-
- [Cards](#cards)
|
|
11
|
-
- [Elevated Card](#elevated-card)
|
|
12
|
-
- [Outlined Card](#outlined-card)
|
|
13
|
-
- [Card with Image Overlay](#card-with-image-overlay)
|
|
14
|
-
- [Horizontal Card](#horizontal-card)
|
|
15
|
-
- [Lists](#lists)
|
|
16
|
-
- [Simple List Item](#simple-list-item)
|
|
17
|
-
- [List with Avatar](#list-with-avatar)
|
|
18
|
-
- [List with Actions](#list-with-actions)
|
|
19
|
-
- [List with Dividers](#list-with-dividers)
|
|
20
|
-
- [ListView with Custom Template](#listview-with-custom-template)
|
|
21
|
-
- [Forms](#forms)
|
|
22
|
-
- [Text Input](#text-input)
|
|
23
|
-
- [Text Area](#text-area)
|
|
24
|
-
- [Password Input](#password-input)
|
|
25
|
-
- [Select/Dropdown (using Picker)](#selectdropdown-using-picker)
|
|
26
|
-
- [Switch Toggle](#switch-toggle)
|
|
27
|
-
- [Slider](#slider)
|
|
28
|
-
- [Form with Validation States](#form-with-validation-states)
|
|
29
|
-
- [Form Section](#form-section)
|
|
30
|
-
- [Buttons](#buttons)
|
|
31
|
-
- [Primary Button](#primary-button)
|
|
32
|
-
- [Secondary Button](#secondary-button)
|
|
33
|
-
- [Outline Button](#outline-button)
|
|
34
|
-
- [Text Button](#text-button)
|
|
35
|
-
- [Icon Button](#icon-button)
|
|
36
|
-
- [Floating Action Button (FAB)](#floating-action-button-fab)
|
|
37
|
-
- [Button with Icon](#button-with-icon)
|
|
38
|
-
- [Button Group](#button-group)
|
|
39
|
-
- [Button Sizes](#button-sizes)
|
|
40
|
-
- [Navigation Bars](#navigation-bars)
|
|
41
|
-
- [Top Navigation Bar with Title](#top-navigation-bar-with-title)
|
|
42
|
-
- [Navigation Bar with Actions](#navigation-bar-with-actions)
|
|
43
|
-
- [Tab Bar (Bottom)](#tab-bar-bottom)
|
|
44
|
-
- [Segment Control](#segment-control)
|
|
45
|
-
- [Breadcrumb Navigation](#breadcrumb-navigation)
|
|
46
|
-
- [Modals and Dialogs](#modals-and-dialogs)
|
|
47
|
-
- [Alert Dialog](#alert-dialog)
|
|
48
|
-
- [Full Screen Modal (iOS)](#full-screen-modal-ios)
|
|
49
|
-
- [Bottom Sheet (Android-style)](#bottom-sheet-android-style)
|
|
50
|
-
- [Toasts and Snackbars](#toasts-and-snackbars)
|
|
51
|
-
- [Simple Toast](#simple-toast)
|
|
52
|
-
- [Custom Snackbar](#custom-snackbar)
|
|
53
|
-
- [Success Toast](#success-toast)
|
|
54
|
-
- [Error Toast](#error-toast)
|
|
55
|
-
- [2. Layout Patterns](#2-layout-patterns)
|
|
56
|
-
- [Screen Structure](#screen-structure)
|
|
57
|
-
- [Standard Screen Layout](#standard-screen-layout)
|
|
58
|
-
- [Tab Bar Screen Structure](#tab-bar-screen-structure)
|
|
59
|
-
- [Spacing System](#spacing-system)
|
|
60
|
-
- [Margin Examples](#margin-examples)
|
|
61
|
-
- [Padding Examples](#padding-examples)
|
|
62
|
-
- [Gap (Spacing between children)](#gap-spacing-between-children)
|
|
63
|
-
- [Responsive Patterns](#responsive-patterns)
|
|
64
|
-
- [Responsive Grid](#responsive-grid)
|
|
65
|
-
- [Adaptive Layout (Tablet vs Phone)](#adaptive-layout-tablet-vs-phone)
|
|
66
|
-
- [Percentage-Based Widths](#percentage-based-widths)
|
|
67
|
-
- [3. Typography](#3-typography)
|
|
68
|
-
- [Font Scales](#font-scales)
|
|
69
|
-
- [Typography Examples](#typography-examples)
|
|
70
|
-
- [Font Weights](#font-weights)
|
|
71
|
-
- [Text Alignment and Truncation](#text-alignment-and-truncation)
|
|
72
|
-
- [Text Alignment](#text-alignment)
|
|
73
|
-
- [Text Truncation](#text-truncation)
|
|
74
|
-
- [Text Transformations](#text-transformations)
|
|
75
|
-
- [4. Colors and Themes](#4-colors-and-themes)
|
|
76
|
-
- [Semantic Color Naming](#semantic-color-naming)
|
|
77
|
-
- [Primary Colors](#primary-colors)
|
|
78
|
-
- [Semantic Colors](#semantic-colors)
|
|
79
|
-
- [Dark Mode Considerations](#dark-mode-considerations)
|
|
80
|
-
- [Platform-Specific Colors](#platform-specific-colors)
|
|
81
|
-
- [iOS System Colors](#ios-system-colors)
|
|
82
|
-
- [Material Design Colors](#material-design-colors)
|
|
83
|
-
- [5. Icons](#5-icons)
|
|
84
|
-
- [Font Awesome 7 Icon Fonts](#font-awesome-7-icon-fonts)
|
|
85
|
-
- [Icon Sizing and Alignment](#icon-sizing-and-alignment)
|
|
86
|
-
- [Icon Sizes](#icon-sizes)
|
|
87
|
-
- [Icon Colors](#icon-colors)
|
|
88
|
-
- [Icon with Text Patterns](#icon-with-text-patterns)
|
|
89
|
-
- [Icon with Label](#icon-with-label)
|
|
90
|
-
- [Circular Icon Button](#circular-icon-button)
|
|
91
|
-
- [Header with Icon Buttons](#header-with-icon-buttons)
|
|
92
|
-
- [Icon Badge](#icon-badge)
|
|
93
|
-
- [Icon Group](#icon-group)
|
|
94
|
-
- [6. Accessibility](#6-accessibility)
|
|
95
|
-
- [Accessibility Labels](#accessibility-labels)
|
|
96
|
-
- [Touch Target Sizes](#touch-target-sizes)
|
|
97
|
-
- [Color Contrast](#color-contrast)
|
|
98
|
-
- [Screen Reader Support](#screen-reader-support)
|
|
99
|
-
- [7. Performance Best Practices](#7-performance-best-practices)
|
|
100
|
-
- [ListView Performance](#listview-performance)
|
|
101
|
-
- [View Recycling](#view-recycling)
|
|
102
|
-
- [Batch Updates](#batch-updates)
|
|
103
|
-
- [Minimize Template Count](#minimize-template-count)
|
|
104
|
-
- [8. Platform-Specific Patterns](#8-platform-specific-patterns)
|
|
105
|
-
- [iOS Patterns](#ios-patterns)
|
|
106
|
-
- [Navigation Window](#navigation-window)
|
|
107
|
-
- [Tab Bar](#tab-bar)
|
|
108
|
-
- [iOS Swipe Actions](#ios-swipe-actions)
|
|
109
|
-
- [Android Patterns](#android-patterns)
|
|
110
|
-
- [Action Bar](#action-bar)
|
|
111
|
-
- [Back Button Handling](#back-button-handling)
|
|
112
|
-
- [Material Design Theme](#material-design-theme)
|
|
113
|
-
- [Notifications](#notifications)
|
|
114
|
-
- [Quick Reference Card](#quick-reference-card)
|
|
115
|
-
- [Common PurgeTSS Classes](#common-purgetss-classes)
|
|
116
|
-
- [Platform Modifiers](#platform-modifiers)
|
|
117
|
-
- [Best Practices Summary](#best-practices-summary)
|
|
118
|
-
- [Additional Resources](#additional-resources)
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
5
|
## 1. Common UI Components
|
|
123
6
|
|
|
124
7
|
### Cards
|
|
@@ -1468,6 +1351,473 @@ Ti.Android.NotificationManager.notify(1, notification)
|
|
|
1468
1351
|
|
|
1469
1352
|
---
|
|
1470
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
|
+
|
|
1471
1821
|
## Quick Reference Card
|
|
1472
1822
|
|
|
1473
1823
|
### Common PurgeTSS Classes
|