ComponentsTemplateSyntaxPage.vala 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. using Spry;
  2. /**
  3. * ComponentsTemplateSyntaxPage - Documentation for Spry template syntax
  4. *
  5. * This page covers all the special HTML attributes and elements used in
  6. * Spry component templates.
  7. */
  8. public class ComponentsTemplateSyntaxPage : PageComponent {
  9. public const string ROUTE = "/components/template-syntax";
  10. public override string markup { get {
  11. return """
  12. <div sid="page" class="doc-content">
  13. <h1>Template Syntax</h1>
  14. <section class="doc-section">
  15. <p>
  16. Spry extends HTML with special attributes that enable reactive behavior,
  17. component composition, and dynamic content. This page covers all the
  18. template syntax available in Spry components.
  19. </p>
  20. </section>
  21. <section class="doc-section">
  22. <h2>Special Attributes</h2>
  23. <h3><code>sid</code> - Spry ID</h3>
  24. <p>
  25. The <code>sid</code> attribute assigns a unique identifier to an element within
  26. a component. Use it to select and manipulate elements in your Vala code.
  27. </p>
  28. <spry-component name="CodeBlockComponent" sid="sid-html"/>
  29. <spry-component name="CodeBlockComponent" sid="sid-vala"/>
  30. </section>
  31. <section class="doc-section">
  32. <h3><code>spry-action</code> - HTMX Action Binding</h3>
  33. <p>
  34. The <code>spry-action</code> attribute binds an element to trigger a component action
  35. when interacted with. The action is sent to the server via HTMX.
  36. </p>
  37. <spry-component name="CodeBlockComponent" sid="action-example"/>
  38. <p>
  39. See the <a href="/components/actions">Actions</a> page for more details.
  40. </p>
  41. </section>
  42. <section class="doc-section">
  43. <h3><code>spry-target</code> - Scoped Targeting</h3>
  44. <p>
  45. The <code>spry-target</code> attribute specifies which element should be replaced
  46. when an action completes. It targets elements by their <code>sid</code> within the
  47. same component.
  48. </p>
  49. <spry-component name="CodeBlockComponent" sid="target-example"/>
  50. </section>
  51. <section class="doc-section">
  52. <h3><code>spry-global</code> - Out-of-Band Swaps</h3>
  53. <p>
  54. The <code>spry-global</code> attribute marks an element for out-of-band updates.
  55. When included in a response, HTMX will swap this element anywhere on the page
  56. that has a matching <code>id</code>.
  57. </p>
  58. <spry-component name="CodeBlockComponent" sid="global-html"/>
  59. <spry-component name="CodeBlockComponent" sid="global-vala"/>
  60. </section>
  61. <section class="doc-section">
  62. <h3><code>*-expr</code> - Expression Attributes</h3>
  63. <p>
  64. Expression attributes dynamically set HTML attributes based on component properties.
  65. Use <code>content-expr</code> for text content, <code>class-*-expr</code> for
  66. conditional classes, and <code>any-attr-expr</code> for any other attribute.
  67. </p>
  68. <spry-component name="CodeBlockComponent" sid="expr-example"/>
  69. </section>
  70. <section class="doc-section">
  71. <h3><code>spry-if</code> / <code>spry-else-if</code> / <code>spry-else</code> - Conditionals</h3>
  72. <p>
  73. Use these attributes for conditional rendering. Only elements with truthy
  74. conditions will be included in the output.
  75. </p>
  76. <spry-component name="CodeBlockComponent" sid="conditional-example"/>
  77. </section>
  78. <section class="doc-section">
  79. <h3><code>spry-per-*</code> - Loop Rendering</h3>
  80. <p>
  81. Use <code>spry-per-{varname}="expression"</code> to iterate over collections.
  82. The variable name becomes available in nested expressions.
  83. </p>
  84. <spry-component name="CodeBlockComponent" sid="loop-example"/>
  85. </section>
  86. <section class="doc-section">
  87. <h2>Special Elements</h2>
  88. <h3><code>&lt;spry-outlet&gt;</code> - Child Component Outlets</h3>
  89. <p>
  90. Outlets are placeholders where child components can be inserted dynamically.
  91. Use <code>set_outlet_children()</code> to populate outlets.
  92. </p>
  93. <spry-component name="CodeBlockComponent" sid="outlet-example"/>
  94. <p>
  95. See the <a href="/components/outlets">Outlets</a> page for more details.
  96. </p>
  97. </section>
  98. <section class="doc-section">
  99. <h3><code>&lt;spry-component&gt;</code> - Declarative Child Components</h3>
  100. <p>
  101. Use <code>&lt;spry-component&gt;</code> to declare child components directly in
  102. your template. Access them with <code>get_component_child&lt;T&gt;()</code>.
  103. </p>
  104. <spry-component name="CodeBlockComponent" sid="component-html"/>
  105. <spry-component name="CodeBlockComponent" sid="component-vala"/>
  106. </section>
  107. <section class="doc-section">
  108. <h3><code>spry-res</code> - Static Resources</h3>
  109. <p>
  110. Use <code>spry-res</code> to reference Spry's built-in static resources.
  111. This resolves to <code>/_spry/res/{resource-name}</code> automatically.
  112. </p>
  113. <spry-component name="CodeBlockComponent" sid="res-example"/>
  114. </section>
  115. <section class="doc-section">
  116. <h2>Next Steps</h2>
  117. <div class="nav-cards">
  118. <a href="/components/actions" class="nav-card">
  119. <h3>Actions →</h3>
  120. <p>Handle user interactions with actions</p>
  121. </a>
  122. <a href="/components/outlets" class="nav-card">
  123. <h3>Outlets →</h3>
  124. <p>Compose components with outlets</p>
  125. </a>
  126. <a href="/components/continuations" class="nav-card">
  127. <h3>Continuations →</h3>
  128. <p>Real-time updates with SSE</p>
  129. </a>
  130. </div>
  131. </section>
  132. </div>
  133. """;
  134. }}
  135. public override async void prepare() throws Error {
  136. // sid examples
  137. var sid_html = get_component_child<CodeBlockComponent>("sid-html");
  138. sid_html.language = "HTML";
  139. sid_html.code = """<div sid="container">
  140. <span sid="title">Hello</span>
  141. <button sid="submit-btn">Submit</button>
  142. </div>""";
  143. var sid_vala = get_component_child<CodeBlockComponent>("sid-vala");
  144. sid_vala.language = "Vala";
  145. sid_vala.code = """// Access elements by sid in prepare() or handle_action()
  146. this["title"].text_content = "Updated Title";
  147. this["submit-btn"].set_attribute("disabled", "true");""";
  148. // spry-action example
  149. var action_example = get_component_child<CodeBlockComponent>("action-example");
  150. action_example.language = "HTML";
  151. action_example.code = """<!-- Same-component action -->
  152. <button spry-action=":Toggle">Toggle</button>
  153. <!-- Cross-component action -->
  154. <button spry-action="ListComponent:Add">Add Item</button>""";
  155. // spry-target example
  156. var target_example = get_component_child<CodeBlockComponent>("target-example");
  157. target_example.language = "HTML";
  158. target_example.code = """<div sid="item" class="item">
  159. <span sid="title">Item Title</span>
  160. <button spry-action=":Delete" spry-target="item">Delete</button>
  161. </div>""";
  162. // spry-global examples
  163. var global_html = get_component_child<CodeBlockComponent>("global-html");
  164. global_html.language = "HTML";
  165. global_html.code = """<!-- In HeaderComponent -->
  166. <div id="header" spry-global="header">
  167. <h1>Todo App</h1>
  168. <span sid="count">0 items</span>
  169. </div>""";
  170. var global_vala = get_component_child<CodeBlockComponent>("global-vala");
  171. global_vala.language = "Vala";
  172. global_vala.code = """// In another component's handle_action()
  173. add_globals_from(header); // Includes header in response for OOB swap""";
  174. // Expression attributes example
  175. var expr_example = get_component_child<CodeBlockComponent>("expr-example");
  176. expr_example.language = "HTML";
  177. expr_example.code = """<!-- Content expression -->
  178. <span content-expr='format("%i%%", this.percent)'>0%</span>
  179. <!-- Style expression -->
  180. <div style-width-expr='format("%i%%", this.percent)'></div>
  181. <!-- Conditional class -->
  182. <div class-completed-expr="this.is_completed">Task</div>
  183. <!-- Any attribute expression -->
  184. <input disabled-expr="this.is_readonly">""";
  185. // Conditional example
  186. var conditional_example = get_component_child<CodeBlockComponent>("conditional-example");
  187. conditional_example.language = "HTML";
  188. conditional_example.code = """<div spry-if="this.is_admin">Admin Panel</div>
  189. <div spry-else-if="this.is_moderator">Moderator Panel</div>
  190. <div spry-else>User Panel</div>""";
  191. // Loop example
  192. var loop_example = get_component_child<CodeBlockComponent>("loop-example");
  193. loop_example.language = "HTML";
  194. loop_example.code = """<ul>
  195. <li spry-per-task="this.tasks">
  196. <span content-expr="task.name"></span>
  197. <span content-expr="task.status"></span>
  198. </li>
  199. </ul>""";
  200. // Outlet example
  201. var outlet_example = get_component_child<CodeBlockComponent>("outlet-example");
  202. outlet_example.language = "HTML";
  203. outlet_example.code = """<div sid="list">
  204. <spry-outlet sid="items"/>
  205. </div>""";
  206. // Component examples
  207. var component_html = get_component_child<CodeBlockComponent>("component-html");
  208. component_html.language = "HTML";
  209. component_html.code = """<div>
  210. <spry-component name="HeaderComponent" sid="header"/>
  211. <spry-component name="TodoListComponent" sid="todo-list"/>
  212. <spry-component name="FooterComponent" sid="footer"/>
  213. </div>""";
  214. var component_vala = get_component_child<CodeBlockComponent>("component-vala");
  215. component_vala.language = "Vala";
  216. component_vala.code = """public override async void prepare() throws Error {
  217. var header = get_component_child<HeaderComponent>("header");
  218. header.title = "My App";
  219. }""";
  220. // Static resources example
  221. var res_example = get_component_child<CodeBlockComponent>("res-example");
  222. res_example.language = "HTML";
  223. res_example.code = """<script spry-res="htmx.js"></script>
  224. <script spry-res="htmx-sse.js"></script>""";
  225. }
  226. }