@indexeddb-orm/idb-orm 0.0.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.
Files changed (216) hide show
  1. package/.vscode/extensions.json +5 -0
  2. package/README.md +1280 -0
  3. package/angular-demo-app/README.md +84 -0
  4. package/angular-demo-app/angular.json +109 -0
  5. package/angular-demo-app/package-lock.json +14215 -0
  6. package/angular-demo-app/package.json +41 -0
  7. package/angular-demo-app/src/app/app.component.ts +481 -0
  8. package/angular-demo-app/src/app/app.routes.ts +8 -0
  9. package/angular-demo-app/src/app/components/actions.component.ts +202 -0
  10. package/angular-demo-app/src/app/components/cloud-sync-demo.component.ts +296 -0
  11. package/angular-demo-app/src/app/components/live-query-demo.component.ts +307 -0
  12. package/angular-demo-app/src/app/components/main-info.component.ts +148 -0
  13. package/angular-demo-app/src/app/components/posts-live-query-demo.component.ts +336 -0
  14. package/angular-demo-app/src/app/components/typescript-demo.component.ts +268 -0
  15. package/angular-demo-app/src/entities/post-tag.entity.ts +25 -0
  16. package/angular-demo-app/src/entities/post.entity.ts +49 -0
  17. package/angular-demo-app/src/entities/profile.entity.ts +42 -0
  18. package/angular-demo-app/src/entities/tag.entity.ts +36 -0
  19. package/angular-demo-app/src/entities/user.entity.ts +59 -0
  20. package/angular-demo-app/src/favicon.ico +1 -0
  21. package/angular-demo-app/src/index.html +16 -0
  22. package/angular-demo-app/src/main.ts +13 -0
  23. package/angular-demo-app/src/services/app-logic.service.ts +449 -0
  24. package/angular-demo-app/src/services/cloud-sync.service.ts +95 -0
  25. package/angular-demo-app/src/services/database.service.ts +26 -0
  26. package/angular-demo-app/src/services/live-query.service.ts +63 -0
  27. package/angular-demo-app/src/services/posts-live-query.service.ts +86 -0
  28. package/angular-demo-app/src/services/typescript-demo.service.ts +59 -0
  29. package/angular-demo-app/src/styles.scss +50 -0
  30. package/angular-demo-app/tsconfig.app.json +13 -0
  31. package/angular-demo-app/tsconfig.json +34 -0
  32. package/angular-demo-app/tsconfig.spec.json +13 -0
  33. package/dist/Database.d.ts +206 -0
  34. package/dist/Database.js +288 -0
  35. package/dist/decorators/Column.d.ts +79 -0
  36. package/dist/decorators/Column.js +236 -0
  37. package/dist/decorators/Entity.d.ts +32 -0
  38. package/dist/decorators/Entity.js +44 -0
  39. package/dist/decorators/Relation.d.ts +70 -0
  40. package/dist/decorators/Relation.js +120 -0
  41. package/dist/decorators/index.d.ts +3 -0
  42. package/dist/decorators/index.js +3 -0
  43. package/dist/errors/ValidationError.d.ts +4 -0
  44. package/dist/errors/ValidationError.js +8 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +7 -0
  47. package/dist/metadata/Column.d.ts +8 -0
  48. package/dist/metadata/Column.js +44 -0
  49. package/dist/metadata/Entity.d.ts +11 -0
  50. package/dist/metadata/Entity.js +21 -0
  51. package/dist/metadata/Relation.d.ts +20 -0
  52. package/dist/metadata/Relation.js +74 -0
  53. package/dist/metadata/index.d.ts +3 -0
  54. package/dist/metadata/index.js +3 -0
  55. package/dist/services/AggregationService.d.ts +38 -0
  56. package/dist/services/AggregationService.js +229 -0
  57. package/dist/services/BaseEntity.d.ts +32 -0
  58. package/dist/services/BaseEntity.js +62 -0
  59. package/dist/services/CloudSyncService.d.ts +100 -0
  60. package/dist/services/CloudSyncService.js +196 -0
  61. package/dist/services/DecoratorUtils.d.ts +12 -0
  62. package/dist/services/DecoratorUtils.js +10 -0
  63. package/dist/services/EntityFactory.d.ts +25 -0
  64. package/dist/services/EntityFactory.js +27 -0
  65. package/dist/services/EntityRegistry.d.ts +61 -0
  66. package/dist/services/EntityRegistry.js +56 -0
  67. package/dist/services/EntitySchema.d.ts +56 -0
  68. package/dist/services/EntitySchema.js +125 -0
  69. package/dist/services/MigrationManager.d.ts +70 -0
  70. package/dist/services/MigrationManager.js +181 -0
  71. package/dist/services/RelationLoader.d.ts +66 -0
  72. package/dist/services/RelationLoader.js +310 -0
  73. package/dist/services/SchemaBuilder.d.ts +68 -0
  74. package/dist/services/SchemaBuilder.js +191 -0
  75. package/dist/services/index.d.ts +7 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/types.d.ts +152 -0
  78. package/dist/types.js +1 -0
  79. package/dist/utils/logger.d.ts +12 -0
  80. package/dist/utils/logger.js +16 -0
  81. package/eslint.config.js +49 -0
  82. package/homepage/favicon.svg +36 -0
  83. package/homepage/index.html +1725 -0
  84. package/package.json +78 -0
  85. package/react-demo-app/README.md +61 -0
  86. package/react-demo-app/eslint.config.js +60 -0
  87. package/react-demo-app/index.html +13 -0
  88. package/react-demo-app/package-lock.json +4955 -0
  89. package/react-demo-app/package.json +39 -0
  90. package/react-demo-app/src/App.tsx +172 -0
  91. package/react-demo-app/src/assets/react.svg +1 -0
  92. package/react-demo-app/src/components/Actions.tsx +171 -0
  93. package/react-demo-app/src/components/CloudSyncDemo.tsx +191 -0
  94. package/react-demo-app/src/components/LiveQueryDemo.tsx +122 -0
  95. package/react-demo-app/src/components/MainInfo.tsx +75 -0
  96. package/react-demo-app/src/components/PostsLiveQueryDemo.tsx +185 -0
  97. package/react-demo-app/src/components/TypeScriptDemo.tsx +190 -0
  98. package/react-demo-app/src/database/Database.ts +30 -0
  99. package/react-demo-app/src/entities/Post.ts +48 -0
  100. package/react-demo-app/src/entities/PostTag.ts +26 -0
  101. package/react-demo-app/src/entities/Profile.ts +41 -0
  102. package/react-demo-app/src/entities/Tag.ts +35 -0
  103. package/react-demo-app/src/entities/User.ts +61 -0
  104. package/react-demo-app/src/hooks/useAppLogic.ts +565 -0
  105. package/react-demo-app/src/hooks/useCloudSyncDemo.ts +84 -0
  106. package/react-demo-app/src/hooks/useLiveQueryDemo.ts +68 -0
  107. package/react-demo-app/src/hooks/usePostsLiveQueryDemo.ts +64 -0
  108. package/react-demo-app/src/hooks/useTypeScriptDemo.ts +43 -0
  109. package/react-demo-app/src/index.css +26 -0
  110. package/react-demo-app/src/main.tsx +18 -0
  111. package/react-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  112. package/react-demo-app/src/migrations/002-add-post-category.ts +37 -0
  113. package/react-demo-app/src/migrations/index.ts +8 -0
  114. package/react-demo-app/src/vite-env.d.ts +1 -0
  115. package/react-demo-app/tsconfig.app.json +22 -0
  116. package/react-demo-app/tsconfig.json +6 -0
  117. package/react-demo-app/vite.config.ts +10 -0
  118. package/src/Database.ts +405 -0
  119. package/src/errors/ValidationError.ts +9 -0
  120. package/src/index.ts +13 -0
  121. package/src/metadata/Column.ts +74 -0
  122. package/src/metadata/Entity.ts +42 -0
  123. package/src/metadata/Relation.ts +121 -0
  124. package/src/metadata/index.ts +5 -0
  125. package/src/services/AggregationService.ts +348 -0
  126. package/src/services/BaseEntity.ts +77 -0
  127. package/src/services/CloudSyncService.ts +248 -0
  128. package/src/services/EntityFactory.ts +35 -0
  129. package/src/services/EntityRegistry.ts +109 -0
  130. package/src/services/EntitySchema.ts +154 -0
  131. package/src/services/MigrationManager.ts +276 -0
  132. package/src/services/RelationLoader.ts +532 -0
  133. package/src/services/SchemaBuilder.ts +237 -0
  134. package/src/services/index.ts +7 -0
  135. package/src/types.d.ts +1 -0
  136. package/src/types.ts +169 -0
  137. package/src/utils/logger.ts +40 -0
  138. package/svelte-demo-app/README.md +61 -0
  139. package/svelte-demo-app/package-lock.json +3000 -0
  140. package/svelte-demo-app/package.json +30 -0
  141. package/svelte-demo-app/src/app.d.ts +12 -0
  142. package/svelte-demo-app/src/app.html +13 -0
  143. package/svelte-demo-app/src/components/Actions.svelte +121 -0
  144. package/svelte-demo-app/src/components/CloudSyncDemo.svelte +333 -0
  145. package/svelte-demo-app/src/components/LiveQueryDemo.svelte +191 -0
  146. package/svelte-demo-app/src/components/MainInfo.svelte +133 -0
  147. package/svelte-demo-app/src/components/PostsLiveQueryDemo.svelte +330 -0
  148. package/svelte-demo-app/src/components/TypeScriptDemo.svelte +251 -0
  149. package/svelte-demo-app/src/database/Database.ts +29 -0
  150. package/svelte-demo-app/src/entities/Post.ts +46 -0
  151. package/svelte-demo-app/src/entities/PostTag.ts +22 -0
  152. package/svelte-demo-app/src/entities/Profile.ts +39 -0
  153. package/svelte-demo-app/src/entities/Tag.ts +33 -0
  154. package/svelte-demo-app/src/entities/User.ts +62 -0
  155. package/svelte-demo-app/src/lib/database/Database.ts +30 -0
  156. package/svelte-demo-app/src/lib/entities/Post.ts +47 -0
  157. package/svelte-demo-app/src/lib/entities/PostTag.ts +23 -0
  158. package/svelte-demo-app/src/lib/entities/Profile.ts +40 -0
  159. package/svelte-demo-app/src/lib/entities/Tag.ts +34 -0
  160. package/svelte-demo-app/src/lib/entities/User.ts +59 -0
  161. package/svelte-demo-app/src/lib/index.ts +7 -0
  162. package/svelte-demo-app/src/lib/migrations/001-add-user-email-index.ts +17 -0
  163. package/svelte-demo-app/src/lib/migrations/002-add-post-category.ts +37 -0
  164. package/svelte-demo-app/src/lib/migrations/index.ts +8 -0
  165. package/svelte-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  166. package/svelte-demo-app/src/migrations/002-add-post-category.ts +37 -0
  167. package/svelte-demo-app/src/migrations/index.ts +8 -0
  168. package/svelte-demo-app/src/routes/+layout.js +3 -0
  169. package/svelte-demo-app/src/routes/+layout.svelte +228 -0
  170. package/svelte-demo-app/src/routes/+page.js +3 -0
  171. package/svelte-demo-app/src/routes/+page.svelte +1305 -0
  172. package/svelte-demo-app/src/stores/appStore.js +603 -0
  173. package/svelte-demo-app/svelte.config.js +18 -0
  174. package/svelte-demo-app/tsconfig.json +14 -0
  175. package/svelte-demo-app/vite.config.ts +6 -0
  176. package/tests/aggregation.e2e.test.ts +87 -0
  177. package/tests/base-entity.e2e.test.ts +47 -0
  178. package/tests/database-api.e2e.test.ts +177 -0
  179. package/tests/decorators.e2e.test.ts +40 -0
  180. package/tests/entity-schema.e2e.test.ts +58 -0
  181. package/tests/relation-loader-table-names.test.ts +192 -0
  182. package/tests/relations.e2e.test.ts +178 -0
  183. package/tests/zod-runtime.e2e.test.ts +69 -0
  184. package/tsconfig.json +21 -0
  185. package/vitest.config.ts +21 -0
  186. package/vitest.setup.ts +27 -0
  187. package/vue-demo-app/README.md +61 -0
  188. package/vue-demo-app/index.html +13 -0
  189. package/vue-demo-app/package-lock.json +1537 -0
  190. package/vue-demo-app/package.json +27 -0
  191. package/vue-demo-app/src/App.vue +100 -0
  192. package/vue-demo-app/src/components/Actions.vue +135 -0
  193. package/vue-demo-app/src/components/CloudSyncDemo.vue +139 -0
  194. package/vue-demo-app/src/components/LiveQueryDemo.vue +122 -0
  195. package/vue-demo-app/src/components/MainInfo.vue +80 -0
  196. package/vue-demo-app/src/components/PostsLiveQueryDemo.vue +136 -0
  197. package/vue-demo-app/src/components/TypeScriptDemo.vue +133 -0
  198. package/vue-demo-app/src/database/Database.ts +29 -0
  199. package/vue-demo-app/src/entities/Post.ts +48 -0
  200. package/vue-demo-app/src/entities/PostTag.ts +24 -0
  201. package/vue-demo-app/src/entities/Profile.ts +41 -0
  202. package/vue-demo-app/src/entities/Tag.ts +35 -0
  203. package/vue-demo-app/src/entities/User.ts +61 -0
  204. package/vue-demo-app/src/main.ts +29 -0
  205. package/vue-demo-app/src/migrations/001-add-user-email-index.ts +23 -0
  206. package/vue-demo-app/src/migrations/002-add-post-category.ts +46 -0
  207. package/vue-demo-app/src/migrations/index.ts +14 -0
  208. package/vue-demo-app/src/services/useAppLogic.ts +565 -0
  209. package/vue-demo-app/src/services/useCloudSyncDemo.ts +84 -0
  210. package/vue-demo-app/src/services/useLiveQueryDemo.ts +82 -0
  211. package/vue-demo-app/src/services/usePostsLiveQueryDemo.ts +77 -0
  212. package/vue-demo-app/src/services/useTypeScriptDemo.ts +56 -0
  213. package/vue-demo-app/src/vite-env.d.ts +1 -0
  214. package/vue-demo-app/tsconfig.json +25 -0
  215. package/vue-demo-app/tsconfig.node.json +10 -0
  216. package/vue-demo-app/vite.config.ts +16 -0
@@ -0,0 +1,1305 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { page } from '$app/stores';
4
+ import { getContext } from 'svelte';
5
+ import { db } from '$lib/database/Database';
6
+ import { newEntity } from 'idb-orm';
7
+ import { UserEntity } from '$lib/entities/User';
8
+ import { PostEntity } from '$lib/entities/Post';
9
+ import { ProfileEntity } from '$lib/entities/Profile';
10
+
11
+ // Get stores from layout
12
+ const users = getContext('users');
13
+ const tabIndex = getContext('tabIndex');
14
+ const mobileOpen = getContext('mobileOpen');
15
+ const syncStatus = getContext('syncStatus');
16
+ const userStats = getContext('userStats');
17
+ const allUsers = getContext('allUsers');
18
+ const allPosts = getContext('allPosts');
19
+ const allProfiles = getContext('allProfiles');
20
+ const postStats = getContext('postStats');
21
+
22
+ // Get functions from layout
23
+ const setTabIndex = getContext('setTabIndex');
24
+ const toggleMobileOpen = getContext('toggleMobileOpen');
25
+ const loadUsers = getContext('loadUsers');
26
+ const addUser = getContext('addUser');
27
+ const testZodValidation = getContext('testZodValidation');
28
+ const testSchemaMigration = getContext('testSchemaMigration');
29
+ const toggleAutoReset = getContext('toggleAutoReset');
30
+ const testRelations = getContext('testRelations');
31
+ const testTransactions = getContext('testTransactions');
32
+ const testCompoundIndexes = getContext('testCompoundIndexes');
33
+ const testAggregations = getContext('testAggregations');
34
+ const clearAll = getContext('clearAll');
35
+
36
+ // Tabs configuration
37
+ const tabs = [
38
+ { key: 'admin', label: 'Admin' },
39
+ { key: 'cloud-sync', label: 'Cloud Sync' }
40
+ ];
41
+
42
+ // Initialize data on mount
43
+ onMount(async () => {
44
+ await loadUsers();
45
+ });
46
+ </script>
47
+
48
+ <!-- Material-UI App Bar -->
49
+ <header class="app-bar">
50
+ <div class="toolbar">
51
+ <button
52
+ class="mobile-menu-button"
53
+ on:click={toggleMobileOpen}
54
+ aria-label="open drawer"
55
+ >
56
+ <svg class="menu-icon" viewBox="0 0 24 24">
57
+ <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
58
+ </svg>
59
+ </button>
60
+ <h1 class="title">Dexie ORM Demo</h1>
61
+ </div>
62
+ </header>
63
+
64
+ <div class="main-layout">
65
+ <!-- Mobile Drawer -->
66
+ <div class="mobile-drawer" class:open={$mobileOpen}>
67
+ <nav class="drawer-content">
68
+ <ul class="nav-list">
69
+ {#each tabs as tab, index}
70
+ <li>
71
+ <button
72
+ class="nav-item"
73
+ class:active={$tabIndex === index}
74
+ on:click={() => {
75
+ setTabIndex(index);
76
+ toggleMobileOpen();
77
+ }}
78
+ >
79
+ {tab.label}
80
+ </button>
81
+ </li>
82
+ {/each}
83
+ </ul>
84
+ </nav>
85
+ </div>
86
+
87
+ <!-- Desktop Drawer -->
88
+ <aside class="desktop-drawer">
89
+ <nav class="drawer-content">
90
+ <ul class="nav-list">
91
+ {#each tabs as tab, index}
92
+ <li>
93
+ <button
94
+ class="nav-item"
95
+ class:active={$tabIndex === index}
96
+ on:click={() => setTabIndex(index)}
97
+ >
98
+ {tab.label}
99
+ </button>
100
+ </li>
101
+ {/each}
102
+ </ul>
103
+ </nav>
104
+ </aside>
105
+
106
+ <!-- Main Content -->
107
+ <main class="main-content">
108
+ {#if $tabIndex === 0}
109
+ <!-- Admin Tab Content - 1:1 with React -->
110
+ <div class="tab-content">
111
+ <!-- Main Info - Material-UI Paper -->
112
+ <div class="main-info">
113
+ <div class="info-card">
114
+ <div class="info-header">
115
+ <svg class="info-icon" viewBox="0 0 24 24">
116
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
117
+ </svg>
118
+ <h2>Dexie ORM for IndexedDB — Overview</h2>
119
+ </div>
120
+ <p class="info-description">
121
+ TypeScript ORM built on the latest Dexie 4 with first-class type safety and Zod runtime validation.
122
+ </p>
123
+ <div class="info-features">
124
+ <div class="feature">
125
+ <svg class="feature-icon" viewBox="0 0 24 24">
126
+ <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0L19.2 12l-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>
127
+ </svg>
128
+ <span><strong>Entity config</strong> via defineEntity()</span>
129
+ </div>
130
+ <div class="feature">
131
+ <svg class="feature-icon" viewBox="0 0 24 24">
132
+ <path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/>
133
+ </svg>
134
+ <span><strong>Automatic schema</strong> with PK, indexes, unique</span>
135
+ </div>
136
+ <div class="feature">
137
+ <svg class="feature-icon" viewBox="0 0 24 24">
138
+ <path d="M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A1.5 1.5 0 0 0 18.54 7H16c-.8 0-1.54.5-1.85 1.26L12.5 14H10v8h2v-6h2.5l2.5 6H20zM12.5 11.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S11 9.17 11 10s.67 1.5 1.5 1.5z"/>
139
+ </svg>
140
+ <span><strong>Relations</strong> with helpers</span>
141
+ </div>
142
+ <div class="feature">
143
+ <svg class="feature-icon" viewBox="0 0 24 24">
144
+ <path d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
145
+ </svg>
146
+ <span><strong>Zod validation</strong> at runtime</span>
147
+ </div>
148
+ <div class="feature">
149
+ <svg class="feature-icon" viewBox="0 0 24 24">
150
+ <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/>
151
+ </svg>
152
+ <span><strong>Aggregations</strong> (count, avg, sum)</span>
153
+ </div>
154
+ <div class="feature">
155
+ <svg class="feature-icon" viewBox="0 0 24 24">
156
+ <path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/>
157
+ </svg>
158
+ <span><strong>Live queries</strong> with useLiveQuery()</span>
159
+ </div>
160
+ <div class="feature">
161
+ <svg class="feature-icon" viewBox="0 0 24 24">
162
+ <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4z"/>
163
+ </svg>
164
+ <span><strong>TypeScript</strong> full type safety</span>
165
+ </div>
166
+ <div class="feature">
167
+ <svg class="feature-icon" viewBox="0 0 24 24">
168
+ <path d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/>
169
+ </svg>
170
+ <span><strong>Cloud sync</strong> with Dexie Cloud</span>
171
+ </div>
172
+ </div>
173
+ <div class="info-button-section">
174
+ <button class="info-button">
175
+ <svg class="button-icon" viewBox="0 0 24 24">
176
+ <path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
177
+ </svg>
178
+ View full documentation
179
+ </button>
180
+ </div>
181
+ </div>
182
+ </div>
183
+
184
+ <!-- Admin Actions - 1:1 z React Material-UI -->
185
+ <div class="admin-section">
186
+ <h2 class="admin-title">Admin Actions</h2>
187
+ <div class="actions-grid">
188
+ <button class="action-button outlined" on:click={addUser}>
189
+ <svg class="action-icon" viewBox="0 0 24 24">
190
+ <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
191
+ </svg>
192
+ Add User
193
+ </button>
194
+
195
+ <button class="action-button outlined" on:click={testZodValidation}>
196
+ <svg class="action-icon" viewBox="0 0 24 24">
197
+ <path d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
198
+ </svg>
199
+ Test Zod
200
+ </button>
201
+
202
+ <button class="action-button outlined" on:click={testSchemaMigration}>
203
+ <svg class="action-icon" viewBox="0 0 24 24">
204
+ <path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/>
205
+ </svg>
206
+ Schema
207
+ </button>
208
+
209
+ <button class="action-button outlined" on:click={toggleAutoReset}>
210
+ <svg class="action-icon" viewBox="0 0 24 24">
211
+ <path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>
212
+ </svg>
213
+ Auto-Reset
214
+ </button>
215
+
216
+ <button class="action-button outlined" on:click={testRelations}>
217
+ <svg class="action-icon" viewBox="0 0 24 24">
218
+ <path d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
219
+ </svg>
220
+ Relations
221
+ </button>
222
+
223
+ <button class="action-button outlined" on:click={testTransactions}>
224
+ <svg class="action-icon" viewBox="0 0 24 24">
225
+ <path d="M13 10V3L4 14h7v7l9-11h-7z"/>
226
+ </svg>
227
+ Transactions
228
+ </button>
229
+
230
+ <button class="action-button outlined" on:click={testCompoundIndexes}>
231
+ <svg class="action-icon" viewBox="0 0 24 24">
232
+ <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
233
+ </svg>
234
+ Indexes
235
+ </button>
236
+
237
+ <button class="action-button outlined" on:click={testAggregations}>
238
+ <svg class="action-icon" viewBox="0 0 24 24">
239
+ <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/>
240
+ </svg>
241
+ Aggregations
242
+ </button>
243
+
244
+ <button class="action-button outlined clear-button" on:click={clearAll}>
245
+ <svg class="action-icon" viewBox="0 0 24 24">
246
+ <path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
247
+ </svg>
248
+ Clear All
249
+ </button>
250
+ </div>
251
+ </div>
252
+
253
+ <!-- Users Section - 1:1 z React Material-UI Paper -->
254
+ {#if $users.length > 0}
255
+ <div class="users-section">
256
+ <h3 class="users-title">Users ({$users.length})</h3>
257
+ <div class="users-stack">
258
+ {#each $users as user}
259
+ <div class="user-paper">
260
+ <strong>{user.name}</strong> ({user.email})<br />
261
+ Age: {user.age}, Tags: {user.tags?.join(', ') || 'none'}
262
+ </div>
263
+ {/each}
264
+ </div>
265
+ </div>
266
+ {/if}
267
+
268
+ <!-- Live Query Demo - 1:1 z React -->
269
+ <div class="live-query-demo">
270
+ <div class="demo-card">
271
+ <div class="demo-header">
272
+ <svg class="demo-icon" viewBox="0 0 24 24">
273
+ <path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/>
274
+ </svg>
275
+ <h2>Live Query Demo</h2>
276
+ </div>
277
+
278
+ <div class="demo-grid">
279
+ <div class="demo-section">
280
+ <div class="section-card">
281
+ <h3 class="section-title">
282
+ <svg class="section-icon" viewBox="0 0 24 24">
283
+ <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/>
284
+ </svg>
285
+ Statistics
286
+ </h3>
287
+ <div class="stats-grid">
288
+ <div class="stat-item">
289
+ <span class="stat-label">Total Users:</span>
290
+ <span class="stat-value">{$allUsers.length}</span>
291
+ </div>
292
+ <div class="stat-item">
293
+ <span class="stat-label">Adult Users:</span>
294
+ <span class="stat-value">{$allUsers.filter(u => u.age >= 18).length}</span>
295
+ </div>
296
+ <div class="stat-item">
297
+ <span class="stat-label">First User:</span>
298
+ <span class="stat-value">{$allUsers[0]?.name || 'None'}</span>
299
+ </div>
300
+ </div>
301
+ </div>
302
+ </div>
303
+
304
+ <div class="demo-section">
305
+ <div class="section-card">
306
+ <h3 class="section-title">
307
+ <svg class="section-icon" viewBox="0 0 24 24">
308
+ <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
309
+ </svg>
310
+ Users with Profiles
311
+ </h3>
312
+ <div class="users-list">
313
+ {#each $allUsers.slice(0, 3) as user}
314
+ <div class="user-item">
315
+ <strong>{user.name}</strong> ({user.email})
316
+ </div>
317
+ {/each}
318
+ </div>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="demo-section">
323
+ <div class="section-card">
324
+ <h3 class="section-title">
325
+ <svg class="section-icon" viewBox="0 0 24 24">
326
+ <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
327
+ </svg>
328
+ Posts
329
+ </h3>
330
+ <div class="posts-list">
331
+ {#each $allPosts.slice(0, 3) as post}
332
+ <div class="post-item">
333
+ <strong>{post.title}</strong>
334
+ <span class="post-status" class:published={post.published}>
335
+ {post.published ? 'Published' : 'Draft'}
336
+ </span>
337
+ </div>
338
+ {/each}
339
+ </div>
340
+ </div>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+
346
+ <!-- TypeScript Demo - 1:1 z React -->
347
+ <div class="typescript-demo">
348
+ <div class="demo-card typescript-card">
349
+ <div class="demo-header">
350
+ <svg class="demo-icon" viewBox="0 0 24 24">
351
+ <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4z"/>
352
+ </svg>
353
+ <h2>TypeScript Demo</h2>
354
+ </div>
355
+
356
+ <div class="demo-content">
357
+ <h3 class="section-title">
358
+ <svg class="section-icon" viewBox="0 0 24 24">
359
+ <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
360
+ </svg>
361
+ Users
362
+ </h3>
363
+ <div class="users-list">
364
+ {#each $allUsers.slice(0, 3) as user}
365
+ <div class="user-item clickable" on:click={() => console.log('User clicked:', user)}>
366
+ <strong>{user.name}</strong> ({user.email})
367
+ </div>
368
+ {/each}
369
+ </div>
370
+
371
+ <h3 class="section-title">
372
+ <svg class="section-icon" viewBox="0 0 24 24">
373
+ <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
374
+ </svg>
375
+ Posts
376
+ </h3>
377
+ <div class="posts-list">
378
+ {#each $allPosts.slice(0, 3) as post}
379
+ <div class="post-item clickable" on:click={() => console.log('Post clicked:', post)}>
380
+ <strong>{post.title}</strong>
381
+ <span class="post-status" class:published={post.published}>
382
+ {post.published ? 'Published' : 'Draft'}
383
+ </span>
384
+ </div>
385
+ {/each}
386
+ </div>
387
+
388
+ <h3 class="section-title">
389
+ <svg class="section-icon" viewBox="0 0 24 24">
390
+ <path d="M12 2C13.1 2 14 2.9 14 4C14 5.1 13.1 6 12 6C10.9 6 10 5.1 10 4C10 2.9 10.9 2 12 2ZM21 9V7L15 1H5C3.89 1 3 1.89 3 3V7H1V9H3V11H1V13H3V15H1V17H3V19C3 20.11 3.89 21 5 21H19C20.11 21 21 20.11 21 19V17H23V15H21V13H23V11H21V9H23V7H21V9Z"/>
391
+ </svg>
392
+ Profiles
393
+ </h3>
394
+ <div class="profiles-list">
395
+ {#each $allProfiles.slice(0, 3) as profile}
396
+ <div class="profile-item clickable" on:click={() => console.log('Profile clicked:', profile)}>
397
+ <strong>{profile.bio || 'No bio'}</strong>
398
+ </div>
399
+ {/each}
400
+ </div>
401
+
402
+ <div class="ide-test">
403
+ <h4>IDE Test</h4>
404
+ <p>Hover over the items above to see TypeScript intellisense in action!</p>
405
+ </div>
406
+ </div>
407
+ </div>
408
+ </div>
409
+
410
+ <!-- Posts Live Query Demo - 1:1 z React -->
411
+ <div class="posts-live-query-demo">
412
+ <div class="demo-card posts-card">
413
+ <div class="demo-header">
414
+ <svg class="demo-icon" viewBox="0 0 24 24">
415
+ <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
416
+ </svg>
417
+ <h2>Posts Live Query Demo</h2>
418
+ </div>
419
+
420
+ <div class="demo-content">
421
+ <h3 class="section-title">
422
+ <svg class="section-icon" viewBox="0 0 24 24">
423
+ <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/>
424
+ </svg>
425
+ Post Statistics
426
+ </h3>
427
+ <div class="stats-grid">
428
+ <div class="stat-item">
429
+ <span class="stat-label">Total Posts:</span>
430
+ <span class="stat-value">{$postStats.total}</span>
431
+ </div>
432
+ <div class="stat-item">
433
+ <span class="stat-label">Published:</span>
434
+ <span class="stat-value">{$postStats.published}</span>
435
+ </div>
436
+ <div class="stat-item">
437
+ <span class="stat-label">Total Likes:</span>
438
+ <span class="stat-value">{$postStats.totalLikes}</span>
439
+ </div>
440
+ </div>
441
+
442
+ <h3 class="section-title">
443
+ <svg class="section-icon" viewBox="0 0 24 24">
444
+ <path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/>
445
+ </svg>
446
+ All Posts
447
+ </h3>
448
+ <div class="posts-list">
449
+ {#each $allPosts.slice(0, 5) as post}
450
+ <div class="post-item">
451
+ <strong>{post.title}</strong>
452
+ <span class="post-status" class:published={post.published}>
453
+ {post.published ? 'Published' : 'Draft'}
454
+ </span>
455
+ <span class="post-likes">{post.likes || 0} likes</span>
456
+ </div>
457
+ {/each}
458
+ </div>
459
+
460
+ <div class="note-section">
461
+ <h4>
462
+ <svg class="note-icon" viewBox="0 0 24 24">
463
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
464
+ </svg>
465
+ Note
466
+ </h4>
467
+ <p>This demonstrates advanced live queries with posts, including statistics and filtering.</p>
468
+ </div>
469
+ </div>
470
+ </div>
471
+ </div>
472
+ </div>
473
+ {:else if $tabIndex === 1}
474
+ <!-- Cloud Sync Tab Content - 1:1 z React -->
475
+ <div class="tab-content">
476
+ <div class="cloud-sync-demo">
477
+ <div class="cloud-sync-card">
478
+ <h2>Cloud Sync Demo</h2>
479
+ <p class="cloud-sync-description">
480
+ <strong>Dexie Cloud Addon Integration:</strong> Cloud synchronization demo
481
+ </p>
482
+
483
+ <div class="cloud-sync-grid">
484
+ <div class="sync-status-section">
485
+ <h3 class="section-title">
486
+ <svg class="section-icon" viewBox="0 0 24 24">
487
+ <path d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/>
488
+ </svg>
489
+ Sync status
490
+ </h3>
491
+ <div class="status-card">
492
+ <p>
493
+ <strong>Enabled:</strong> {$syncStatus.enabled ? 'Yes' : 'No'}
494
+ </p>
495
+ <p>
496
+ <strong>Online:</strong> {$syncStatus.isOnline !== undefined ? ($syncStatus.isOnline ? 'Yes' : 'No') : 'Unknown'}
497
+ </p>
498
+ <p>
499
+ <strong>Last sync:</strong> {$syncStatus.lastSync ? new Date($syncStatus.lastSync).toLocaleString() : 'Never'}
500
+ </p>
501
+ </div>
502
+ </div>
503
+
504
+ <div class="sync-controls-section">
505
+ <h3 class="section-title">
506
+ <svg class="section-icon" viewBox="0 0 24 24">
507
+ <path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>
508
+ </svg>
509
+ Sync controls
510
+ </h3>
511
+ <div class="controls-buttons">
512
+ <button
513
+ class="sync-button manual"
514
+ on:click={() => alert('Sync manually')}
515
+ disabled={!$syncStatus.enabled}
516
+ >
517
+ Sync manually
518
+ </button>
519
+
520
+ <button
521
+ class="sync-button tables"
522
+ on:click={() => alert('Sync tables (users, posts)')}
523
+ disabled={!$syncStatus.enabled}
524
+ >
525
+ Sync tables (users, posts)
526
+ </button>
527
+
528
+ {#if !$syncStatus.enabled}
529
+ <button
530
+ class="sync-button enable"
531
+ on:click={() => alert('Enable cloud sync')}
532
+ >
533
+ Enable cloud sync
534
+ </button>
535
+ {:else}
536
+ <button
537
+ class="sync-button disable"
538
+ on:click={() => alert('Disable cloud sync')}
539
+ >
540
+ Disable cloud sync
541
+ </button>
542
+ {/if}
543
+ </div>
544
+ </div>
545
+ </div>
546
+
547
+ <div class="instructions-section">
548
+ <h4 class="instructions-title">
549
+ <svg class="instructions-icon" viewBox="0 0 24 24">
550
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
551
+ </svg>
552
+ Instructions:
553
+ </h4>
554
+ <ol class="instructions-list">
555
+ <li>
556
+ <strong>Install dexie-cloud-addon:</strong> <code>npm install dexie-cloud-addon</code>
557
+ </li>
558
+ <li>
559
+ <strong>Configure database URL</strong> in <code>demo-app/src/database/Database.ts</code>
560
+ </li>
561
+ <li>
562
+ <strong>Enable cloud sync</strong> using the button above
563
+ </li>
564
+ <li>
565
+ <strong>Test sync</strong> – add data and verify it synchronizes
566
+ </li>
567
+ </ol>
568
+ </div>
569
+
570
+ <div class="configuration-section">
571
+ <strong>Configuration:</strong> Cloud sync is configured in Database.ts with automatic sync every 30 seconds.
572
+ <br />
573
+ <strong>Offline Support:</strong> The app works offline and syncs when connection is restored.
574
+ <br />
575
+ <strong>Reactivity:</strong> All changes are automatically synchronized with the cloud!
576
+ </div>
577
+ </div>
578
+ </div>
579
+ </div>
580
+ {/if}
581
+ </main>
582
+ </div>
583
+
584
+ <style>
585
+ /* Global styles for full width */
586
+ :global(body), :global(html) {
587
+ margin: 0;
588
+ padding: 0;
589
+ width: 100%;
590
+ height: 100%;
591
+ }
592
+
593
+ :global(#app) {
594
+ width: 100%;
595
+ height: 100%;
596
+ }
597
+
598
+ /* Material-UI App Bar */
599
+ .app-bar {
600
+ background-color: #1976d2;
601
+ color: white;
602
+ box-shadow: 0px 2px 4px -1px rgba(0,0,0,0.2), 0px 4px 5px 0px rgba(0,0,0,0.14), 0px 1px 10px 0px rgba(0,0,0,0.12);
603
+ z-index: 1000;
604
+ width: 100%;
605
+ position: static;
606
+ }
607
+
608
+ .toolbar {
609
+ display: flex;
610
+ align-items: center;
611
+ padding: 0 16px;
612
+ height: 64px;
613
+ }
614
+
615
+ .mobile-menu-button {
616
+ display: none;
617
+ background: none;
618
+ border: none;
619
+ color: white;
620
+ cursor: pointer;
621
+ padding: 8px;
622
+ margin-right: 16px;
623
+ }
624
+
625
+ .menu-icon {
626
+ width: 24px;
627
+ height: 24px;
628
+ fill: currentColor;
629
+ }
630
+
631
+ .title {
632
+ font-size: 1.25rem;
633
+ font-weight: 500;
634
+ margin: 0;
635
+ flex-grow: 1;
636
+ }
637
+
638
+ .main-layout {
639
+ display: flex;
640
+ flex: 1;
641
+ width: 100%;
642
+ min-height: calc(100vh - 64px);
643
+ }
644
+
645
+ /* Material-UI Drawer */
646
+ .mobile-drawer {
647
+ position: fixed;
648
+ top: 0;
649
+ left: 0;
650
+ height: 100vh;
651
+ width: 240px;
652
+ background: white;
653
+ box-shadow: 0px 8px 10px -5px rgba(0,0,0,0.2), 0px 16px 24px 2px rgba(0,0,0,0.14), 0px 6px 30px 5px rgba(0,0,0,0.12);
654
+ z-index: 1001;
655
+ transform: translateX(-100%);
656
+ transition: transform 0.3s ease;
657
+ padding-top: 64px;
658
+ }
659
+
660
+ .mobile-drawer.open {
661
+ transform: translateX(0);
662
+ }
663
+
664
+ .desktop-drawer {
665
+ display: none;
666
+ width: 240px;
667
+ background: white;
668
+ box-shadow: 0px 8px 10px -5px rgba(0,0,0,0.2), 0px 16px 24px 2px rgba(0,0,0,0.14), 0px 6px 30px 5px rgba(0,0,0,0.12);
669
+ flex-shrink: 0;
670
+ }
671
+
672
+ .drawer-content {
673
+ padding: 16px 0;
674
+ }
675
+
676
+ .nav-list {
677
+ list-style: none;
678
+ margin: 0;
679
+ padding: 0;
680
+ }
681
+
682
+ .nav-item {
683
+ display: block;
684
+ width: 100%;
685
+ padding: 12px 24px;
686
+ border: none;
687
+ background: none;
688
+ text-align: left;
689
+ cursor: pointer;
690
+ color: #666;
691
+ transition: background-color 0.2s;
692
+ }
693
+
694
+ .nav-item:hover {
695
+ background-color: #f5f5f5;
696
+ }
697
+
698
+ .nav-item.active {
699
+ background-color: #e3f2fd;
700
+ color: #1976d2;
701
+ font-weight: 500;
702
+ }
703
+
704
+ .main-content {
705
+ flex-grow: 1;
706
+ padding: 24px;
707
+ width: 100%;
708
+ max-width: 100%;
709
+ }
710
+
711
+ .tab-content {
712
+ width: 100%;
713
+ }
714
+
715
+ /* Material-UI Paper for Main Info */
716
+ .main-info {
717
+ margin-bottom: 24px;
718
+ }
719
+
720
+ .info-card {
721
+ background: white;
722
+ border: 1px solid #e0e0e0;
723
+ border-radius: 4px;
724
+ padding: 16px;
725
+ box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12);
726
+ }
727
+
728
+ .info-header {
729
+ display: flex;
730
+ align-items: center;
731
+ margin-bottom: 16px;
732
+ }
733
+
734
+ .info-icon {
735
+ width: 24px;
736
+ height: 24px;
737
+ margin-right: 8px;
738
+ fill: #1976d2;
739
+ }
740
+
741
+ .info-header h2 {
742
+ margin: 0;
743
+ font-size: 1.25rem;
744
+ font-weight: 500;
745
+ color: #1976d2;
746
+ }
747
+
748
+ .info-description {
749
+ font-size: 0.875rem;
750
+ color: rgba(0, 0, 0, 0.6);
751
+ margin-bottom: 16px;
752
+ line-height: 1.43;
753
+ }
754
+
755
+ .info-features {
756
+ display: grid;
757
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
758
+ gap: 8px;
759
+ margin-bottom: 16px;
760
+ }
761
+
762
+ .feature {
763
+ display: flex;
764
+ align-items: center;
765
+ gap: 8px;
766
+ }
767
+
768
+ .feature-icon {
769
+ width: 16px;
770
+ height: 16px;
771
+ fill: #1976d2;
772
+ }
773
+
774
+ .feature span {
775
+ font-size: 0.875rem;
776
+ color: rgba(0, 0, 0, 0.87);
777
+ }
778
+
779
+ .info-button-section {
780
+ margin-top: 16px;
781
+ padding-top: 16px;
782
+ border-top: 1px solid #e0e0e0;
783
+ }
784
+
785
+ .info-button {
786
+ display: inline-flex;
787
+ align-items: center;
788
+ padding: 6px 16px;
789
+ background: transparent;
790
+ color: #1976d2;
791
+ border: 1px solid #1976d2;
792
+ border-radius: 4px;
793
+ cursor: pointer;
794
+ font-size: 0.875rem;
795
+ font-weight: 500;
796
+ text-transform: uppercase;
797
+ transition: background-color 0.2s;
798
+ }
799
+
800
+ .info-button:hover {
801
+ background-color: rgba(25, 118, 210, 0.04);
802
+ }
803
+
804
+ .button-icon {
805
+ width: 16px;
806
+ height: 16px;
807
+ margin-right: 8px;
808
+ fill: currentColor;
809
+ }
810
+
811
+ /* Admin Actions - 1:1 z React Material-UI */
812
+ .admin-section {
813
+ margin-bottom: 24px;
814
+ }
815
+
816
+ .admin-title {
817
+ font-size: 1.5rem;
818
+ font-weight: 400;
819
+ margin: 0 0 16px 0;
820
+ color: rgba(0, 0, 0, 0.87);
821
+ line-height: 1.33;
822
+ }
823
+
824
+ .actions-grid {
825
+ display: grid;
826
+ grid-template-columns: repeat(2, 1fr);
827
+ grid-template-rows: repeat(5, auto);
828
+ gap: 8px;
829
+ margin-bottom: 16px;
830
+ }
831
+
832
+ .action-button {
833
+ display: flex;
834
+ align-items: center;
835
+ justify-content: center;
836
+ gap: 8px;
837
+ padding: 8px 16px;
838
+ border: 1px solid rgba(25, 118, 210, 0.5);
839
+ background: transparent;
840
+ border-radius: 4px;
841
+ cursor: pointer;
842
+ font-size: 0.75rem;
843
+ font-weight: 500;
844
+ color: #1976d2;
845
+ text-transform: uppercase;
846
+ transition: all 0.2s;
847
+ min-height: 36px;
848
+ }
849
+
850
+ .action-button.outlined {
851
+ border: 1px solid rgba(25, 118, 210, 0.5);
852
+ background: transparent;
853
+ color: #1976d2;
854
+ }
855
+
856
+ .action-button.outlined:hover {
857
+ background: rgba(25, 118, 210, 0.04);
858
+ border-color: #1976d2;
859
+ }
860
+
861
+ .action-button.clear-button {
862
+ /* Clear All będzie w linii z innymi przyciskami */
863
+ }
864
+
865
+ .action-icon {
866
+ width: 18px;
867
+ height: 18px;
868
+ fill: currentColor;
869
+ }
870
+
871
+ /* Users Section - 1:1 z React Material-UI */
872
+ .users-section {
873
+ margin-top: 24px;
874
+ }
875
+
876
+ .users-title {
877
+ font-size: 1.25rem;
878
+ font-weight: 500;
879
+ margin: 0 0 8px 0;
880
+ color: rgba(0, 0, 0, 0.87);
881
+ line-height: 1.6;
882
+ }
883
+
884
+ .users-stack {
885
+ display: flex;
886
+ flex-direction: column;
887
+ gap: 8px;
888
+ }
889
+
890
+ .user-paper {
891
+ background: white;
892
+ border: 1px solid rgba(0, 0, 0, 0.12);
893
+ border-radius: 4px;
894
+ padding: 12px;
895
+ box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12);
896
+ font-size: 0.875rem;
897
+ line-height: 1.43;
898
+ }
899
+
900
+ /* Cloud Sync Demo - 1:1 z React */
901
+ .cloud-sync-demo {
902
+ margin: 24px 0;
903
+ }
904
+
905
+ .cloud-sync-card {
906
+ border: 2px solid #2196f3;
907
+ padding: 20px;
908
+ margin: 20px 0;
909
+ border-radius: 8px;
910
+ background-color: #e3f2fd;
911
+ }
912
+
913
+ .cloud-sync-card h2 {
914
+ margin: 0 0 16px 0;
915
+ font-size: 1.5rem;
916
+ font-weight: 500;
917
+ color: #1976d2;
918
+ }
919
+
920
+ .cloud-sync-description {
921
+ color: #666;
922
+ margin-bottom: 20px;
923
+ font-size: 0.875rem;
924
+ line-height: 1.43;
925
+ }
926
+
927
+ .cloud-sync-grid {
928
+ display: grid;
929
+ grid-template-columns: 1fr 1fr;
930
+ gap: 20px;
931
+ margin-bottom: 20px;
932
+ }
933
+
934
+ .sync-status-section, .sync-controls-section {
935
+ display: flex;
936
+ flex-direction: column;
937
+ }
938
+
939
+ .section-title {
940
+ display: flex;
941
+ align-items: center;
942
+ gap: 8px;
943
+ margin: 0 0 12px 0;
944
+ font-size: 1rem;
945
+ font-weight: 500;
946
+ color: #1976d2;
947
+ }
948
+
949
+ .section-icon {
950
+ width: 20px;
951
+ height: 20px;
952
+ fill: #1976d2;
953
+ }
954
+
955
+ .status-card {
956
+ background-color: white;
957
+ padding: 15px;
958
+ border-radius: 4px;
959
+ border: 1px solid #ddd;
960
+ }
961
+
962
+ .status-card p {
963
+ margin: 0 0 8px 0;
964
+ font-size: 0.875rem;
965
+ line-height: 1.43;
966
+ }
967
+
968
+ .status-card p:last-child {
969
+ margin-bottom: 0;
970
+ }
971
+
972
+ .controls-buttons {
973
+ display: flex;
974
+ flex-direction: column;
975
+ gap: 10px;
976
+ }
977
+
978
+ .sync-button {
979
+ padding: 10px 15px;
980
+ border: none;
981
+ border-radius: 4px;
982
+ cursor: pointer;
983
+ font-size: 0.875rem;
984
+ font-weight: 500;
985
+ transition: background-color 0.2s;
986
+ }
987
+
988
+ .sync-button.manual {
989
+ background-color: #4caf50;
990
+ color: white;
991
+ }
992
+
993
+ .sync-button.manual:disabled {
994
+ background-color: #ccc;
995
+ cursor: not-allowed;
996
+ }
997
+
998
+ .sync-button.tables {
999
+ background-color: #ff9800;
1000
+ color: white;
1001
+ }
1002
+
1003
+ .sync-button.tables:disabled {
1004
+ background-color: #ccc;
1005
+ cursor: not-allowed;
1006
+ }
1007
+
1008
+ .sync-button.enable {
1009
+ background-color: #2196f3;
1010
+ color: white;
1011
+ }
1012
+
1013
+ .sync-button.disable {
1014
+ background-color: #f44336;
1015
+ color: white;
1016
+ }
1017
+
1018
+ .instructions-section {
1019
+ background-color: #fff3e0;
1020
+ padding: 15px;
1021
+ border-radius: 4px;
1022
+ border: 1px solid #ffb74d;
1023
+ margin-bottom: 15px;
1024
+ }
1025
+
1026
+ .instructions-title {
1027
+ display: flex;
1028
+ align-items: center;
1029
+ gap: 8px;
1030
+ margin: 0 0 16px 0;
1031
+ font-size: 1rem;
1032
+ font-weight: 500;
1033
+ color: #1976d2;
1034
+ }
1035
+
1036
+ .instructions-icon {
1037
+ width: 16px;
1038
+ height: 16px;
1039
+ fill: #1976d2;
1040
+ }
1041
+
1042
+ .instructions-list {
1043
+ margin: 10px 0;
1044
+ padding-left: 20px;
1045
+ font-size: 0.875rem;
1046
+ line-height: 1.43;
1047
+ }
1048
+
1049
+ .instructions-list li {
1050
+ margin-bottom: 8px;
1051
+ }
1052
+
1053
+ .instructions-list code {
1054
+ background-color: #f5f5f5;
1055
+ padding: 2px 4px;
1056
+ border-radius: 3px;
1057
+ font-family: 'Courier New', monospace;
1058
+ font-size: 0.8rem;
1059
+ }
1060
+
1061
+ .configuration-section {
1062
+ margin-top: 15px;
1063
+ font-size: 0.9em;
1064
+ color: #666;
1065
+ background-color: #e8f5e8;
1066
+ padding: 10px;
1067
+ border-radius: 4px;
1068
+ line-height: 1.43;
1069
+ }
1070
+
1071
+ /* Responsive Design */
1072
+ @media (max-width: 768px) {
1073
+ .cloud-sync-grid {
1074
+ grid-template-columns: 1fr;
1075
+ }
1076
+ }
1077
+
1078
+ /* Live Query Demo */
1079
+ .live-query-demo {
1080
+ margin: 24px 0;
1081
+ }
1082
+
1083
+ .demo-header {
1084
+ display: flex;
1085
+ align-items: center;
1086
+ margin-bottom: 16px;
1087
+ }
1088
+
1089
+ .demo-icon {
1090
+ width: 24px;
1091
+ height: 24px;
1092
+ margin-right: 8px;
1093
+ fill: #1976d2;
1094
+ }
1095
+
1096
+ .demo-header h2 {
1097
+ margin: 0;
1098
+ font-size: 1.25rem;
1099
+ font-weight: 500;
1100
+ color: #1976d2;
1101
+ }
1102
+
1103
+ .demo-grid {
1104
+ display: grid;
1105
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1106
+ gap: 16px;
1107
+ margin-top: 16px;
1108
+ }
1109
+
1110
+ .demo-section {
1111
+ display: flex;
1112
+ flex-direction: column;
1113
+ }
1114
+
1115
+ .section-card {
1116
+ background: white;
1117
+ border: 1px solid #e0e0e0;
1118
+ border-radius: 4px;
1119
+ padding: 16px;
1120
+ box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12);
1121
+ flex: 1;
1122
+ display: flex;
1123
+ flex-direction: column;
1124
+ }
1125
+
1126
+ .section-title {
1127
+ display: flex;
1128
+ align-items: center;
1129
+ gap: 8px;
1130
+ font-size: 1rem;
1131
+ font-weight: 500;
1132
+ margin: 0 0 12px 0;
1133
+ color: rgba(0, 0, 0, 0.87);
1134
+ }
1135
+
1136
+ .stats-grid {
1137
+ display: flex;
1138
+ flex-direction: column;
1139
+ gap: 8px;
1140
+ }
1141
+
1142
+ .stat-item {
1143
+ display: flex;
1144
+ justify-content: space-between;
1145
+ padding: 4px 0;
1146
+ border-bottom: 1px solid #e0e0e0;
1147
+ }
1148
+
1149
+ .stat-label {
1150
+ font-weight: 500;
1151
+ color: rgba(0, 0, 0, 0.6);
1152
+ }
1153
+
1154
+ .stat-value {
1155
+ color: #1976d2;
1156
+ font-weight: 500;
1157
+ }
1158
+
1159
+ .user-item, .post-item, .profile-item {
1160
+ padding: 8px 0;
1161
+ border-bottom: 1px solid #e0e0e0;
1162
+ font-size: 0.875rem;
1163
+ }
1164
+
1165
+ .user-item:last-child, .post-item:last-child, .profile-item:last-child {
1166
+ border-bottom: none;
1167
+ }
1168
+
1169
+ .clickable {
1170
+ cursor: pointer;
1171
+ transition: background-color 0.2s;
1172
+ }
1173
+
1174
+ .clickable:hover {
1175
+ background-color: rgba(25, 118, 210, 0.04);
1176
+ }
1177
+
1178
+ .post-status {
1179
+ display: inline-block;
1180
+ padding: 2px 8px;
1181
+ border-radius: 12px;
1182
+ font-size: 0.75rem;
1183
+ font-weight: 500;
1184
+ background-color: #f44336;
1185
+ color: white;
1186
+ margin-left: 8px;
1187
+ }
1188
+
1189
+ .post-status.published {
1190
+ background-color: #4caf50;
1191
+ }
1192
+
1193
+ .post-likes {
1194
+ color: #666;
1195
+ font-size: 0.75rem;
1196
+ margin-left: 8px;
1197
+ }
1198
+
1199
+ /* TypeScript Demo - kolorowe tło jak w React */
1200
+ .typescript-demo {
1201
+ margin: 24px 0;
1202
+ }
1203
+
1204
+ .typescript-card {
1205
+ border: 2px solid #ff9800;
1206
+ background-color: #fff8e1;
1207
+ border-radius: 8px;
1208
+ }
1209
+
1210
+ .ide-test {
1211
+ margin-top: 16px;
1212
+ padding: 12px;
1213
+ background-color: rgba(255, 152, 0, 0.1);
1214
+ border-radius: 4px;
1215
+ }
1216
+
1217
+ .ide-test h4 {
1218
+ margin: 0 0 8px 0;
1219
+ font-size: 1rem;
1220
+ font-weight: 500;
1221
+ }
1222
+
1223
+ .ide-test p {
1224
+ margin: 0;
1225
+ font-size: 0.875rem;
1226
+ color: rgba(0, 0, 0, 0.6);
1227
+ }
1228
+
1229
+ /* Posts Live Query Demo - kolorowe tło jak w React */
1230
+ .posts-live-query-demo {
1231
+ margin: 24px 0;
1232
+ }
1233
+
1234
+ .posts-card {
1235
+ border: 2px solid #9c27b0;
1236
+ background-color: #faf5ff;
1237
+ border-radius: 8px;
1238
+ }
1239
+
1240
+ .note-section {
1241
+ margin-top: 16px;
1242
+ padding: 12px;
1243
+ background-color: rgba(156, 39, 176, 0.1);
1244
+ border-radius: 4px;
1245
+ }
1246
+
1247
+ .note-section h4 {
1248
+ display: flex;
1249
+ align-items: center;
1250
+ gap: 8px;
1251
+ margin: 0 0 8px 0;
1252
+ font-size: 1rem;
1253
+ font-weight: 500;
1254
+ }
1255
+
1256
+ .note-icon {
1257
+ width: 16px;
1258
+ height: 16px;
1259
+ fill: #9c27b0;
1260
+ }
1261
+
1262
+ .note-section p {
1263
+ margin: 0;
1264
+ font-size: 0.875rem;
1265
+ color: rgba(0, 0, 0, 0.6);
1266
+ }
1267
+
1268
+ /* Responsive Design - 1:1 z React Material-UI */
1269
+ @media (min-width: 600px) {
1270
+ .actions-grid {
1271
+ grid-template-columns: repeat(3, 1fr);
1272
+ grid-template-rows: repeat(3, auto);
1273
+ }
1274
+ }
1275
+
1276
+ @media (min-width: 960px) {
1277
+ .mobile-menu-button {
1278
+ display: none;
1279
+ }
1280
+
1281
+ .desktop-drawer {
1282
+ display: block;
1283
+ }
1284
+
1285
+ .main-content {
1286
+ width: 100%;
1287
+ }
1288
+
1289
+ .actions-grid {
1290
+ grid-template-columns: repeat(4, 1fr);
1291
+ grid-template-rows: repeat(3, auto);
1292
+ }
1293
+
1294
+ .demo-grid {
1295
+ grid-template-columns: repeat(3, 1fr);
1296
+ }
1297
+ }
1298
+
1299
+ @media (min-width: 1200px) {
1300
+ .actions-grid {
1301
+ grid-template-columns: repeat(5, 1fr);
1302
+ grid-template-rows: repeat(2, auto);
1303
+ }
1304
+ }
1305
+ </style>