basecoat 0.1.3 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dadf44785123af07635692a4f315fb01b9643fdd78ca5ad069f3a102569bcaf0
4
- data.tar.gz: fcf782397f0232aa0e99456d80fb8169639d64a3aed3b910e39ecc8a5ad192cd
3
+ metadata.gz: 17085310ec6fbb84cd7e779e04cf55362947f1cf440c7d92d5762d2e5759bf6c
4
+ data.tar.gz: 8370e2d4f4237d2f8d59ca917ca21edcacbec77cf45a4ee9acbfdc841a4bdff1
5
5
  SHA512:
6
- metadata.gz: ae5d800a24e3cbbfdf80577591615678ea1e176033b2e3ee5c0827f33d418e2bb30cf290d2329838ea13e8f588717dbf278ef6a6b86bfc24ebb1e318838a3e85
7
- data.tar.gz: 03706d6dc426530b68bfa825de75e66ffd97138f268a6ce64e498ec02192650487f73a4d25dd3eb23af0c1a0fa88c7ee209020da1227ce18d1b3112b4652eec0
6
+ metadata.gz: 5e8baf2623834233d0dfeb1dc08939ca7ab3f0e0994ff2a0401b7b40f0e05ec948c072d8eb840c1afd20b3b1073b55240106bebf663dadb17e31e98733c56165
7
+ data.tar.gz: bd091f59684a2d9e5370b46e2f7bce65c0cc8e3d658c9153adc2d671ce6380c786577fb46fd1e83711d4b68afd840ae5668b194b37e09b6d9d9487d150033295
@@ -0,0 +1,5 @@
1
+ <component name="ProjectCodeStyleConfiguration">
2
+ <state>
3
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
4
+ </state>
5
+ </component>
data/README.md CHANGED
@@ -167,6 +167,7 @@ The authentication views include:
167
167
  - Tailwind CSS ([installation instructions](https://github.com/rails/tailwindcss-rails))
168
168
  - Basecoat CSS
169
169
  - Turbo Rails (for scaffold templates)
170
+ - Stimulus (for the theme toggle, can be moved to something else if you desire...)
170
171
 
171
172
  ## How It Works
172
173
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basecoat
4
- VERSION = "0.1.3"
4
+ VERSION = "1.0.1"
5
5
  end
@@ -6,7 +6,7 @@
6
6
  <%= render "layouts/aside" %>
7
7
  <main>
8
8
  <%= render "layouts/header" %>
9
- <div class="py-8">
9
+ <div class="py-8 px-4">
10
10
  <%= turbo_frame_tag "main_content" do %>
11
11
  <%= yield %>
12
12
  <% end %>
@@ -5,17 +5,14 @@
5
5
  <%= render 'layouts/notice', notice: notice if notice %>
6
6
  <div class="grid min-h-svh lg:grid-cols-2">
7
7
  <div class="flex flex-col gap-4 p-6 md:p-10">
8
- <div class="flex justify-between items-center gap-2">
8
+ <div class="flex justify-between items-center gap-2" data-controller="theme">
9
9
  <a href="#" class="flex items-center gap-2 font-medium">
10
10
  <div class="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md">
11
11
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="h-4 w-4"><rect width="256" height="256" fill="none"></rect><line x1="208" y1="128" x2="128" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line><line x1="192" y1="40" x2="40" y2="192" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line></svg>
12
12
  </div>
13
13
  Basecoat
14
14
  </a>
15
- <button type="button" aria-label="Toggle dark mode" data-tooltip="Toggle dark mode" data-side="left" onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))" class="btn-icon-outline size-8">
16
- <span class="hidden dark:block"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg></span>
17
- <span class="block dark:hidden"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg></span>
18
- </button>
15
+ <%= render "layouts/theme_toggle" %>
19
16
  </div>
20
17
  <%= render 'layouts/alert', alert: alert if alert %>
21
18
 
@@ -1,12 +1,9 @@
1
- <header class="bg-background sticky inset-x-0 top-0 isolate flex shrink-0 items-center gap-2 border-b z-10 px-4">
1
+ <header class="bg-background sticky inset-x-0 top-0 isolate flex shrink-0 items-center gap-2 border-b z-10 px-4" data-controller="theme">
2
2
  <div class="flex h-14 flex-1 items-center gap-2">
3
3
  <button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))" aria-label="Toggle sidebar" data-tooltip="Toggle sidebar" data-side="bottom" data-align="start" class="btn-sm-icon-ghost mr-auto size-7 -ml-1.5">
4
4
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M9 3v18"></path></svg>
5
5
  </button>
6
- <button type="button" aria-label="Toggle dark mode" data-tooltip="Toggle dark mode" data-side="left" onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))" class="btn-icon-outline size-8">
7
- <span class="hidden dark:block"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg></span>
8
- <span class="block dark:hidden"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg></span>
9
- </button>
6
+ <%= render "layouts/theme_toggle" %>
10
7
  <!-- DEVISE_USER_DROPDOWN -->
11
8
  </div>
12
9
  </header>
@@ -0,0 +1,4 @@
1
+ <button type="button" aria-label="Toggle dark mode" data-tooltip="Toggle dark mode" data-side="left" data-action="click->theme#toggle" class="btn-icon-outline size-8">
2
+ <span class="hidden dark:block"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg></span>
3
+ <span class="block dark:hidden"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg></span>
4
+ </button>
@@ -5,17 +5,14 @@
5
5
  <%= render 'layouts/notice', notice: notice if notice %>
6
6
  <div class="grid min-h-svh lg:grid-cols-2">
7
7
  <div class="flex flex-col gap-4 p-6 md:p-10">
8
- <div class="flex justify-between items-center gap-2">
8
+ <div class="flex justify-between items-center gap-2" data-controller="theme">
9
9
  <a href="#" class="flex items-center gap-2 font-medium">
10
10
  <div class="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md">
11
11
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="h-4 w-4"><rect width="256" height="256" fill="none"></rect><line x1="208" y1="128" x2="128" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line><line x1="192" y1="40" x2="40" y2="192" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"></line></svg>
12
12
  </div>
13
13
  Basecoat
14
14
  </a>
15
- <button type="button" aria-label="Toggle dark mode" data-tooltip="Toggle dark mode" data-side="left" onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))" class="btn-icon-outline size-8">
16
- <span class="hidden dark:block"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg></span>
17
- <span class="block dark:hidden"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg></span>
18
- </button>
15
+ <%= render "layouts/theme_toggle" %>
19
16
  </div>
20
17
  <%= render 'layouts/alert', alert: alert if alert %>
21
18
 
@@ -0,0 +1,29 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ // Apply theme on initial load (runs immediately to prevent flash)
6
+ this.applyStoredTheme()
7
+ }
8
+
9
+ toggle() {
10
+ const isDark = !document.documentElement.classList.contains('dark')
11
+ this.apply(isDark)
12
+ }
13
+
14
+ apply(dark) {
15
+ document.documentElement.classList.toggle('dark', dark)
16
+ try {
17
+ localStorage.setItem('themeMode', dark ? 'dark' : 'light')
18
+ } catch (_) {}
19
+ }
20
+
21
+ applyStoredTheme() {
22
+ try {
23
+ const stored = localStorage.getItem('themeMode')
24
+ if (stored ? stored === 'dark' : matchMedia('(prefers-color-scheme: dark)').matches) {
25
+ document.documentElement.classList.add('dark')
26
+ }
27
+ } catch (_) {}
28
+ }
29
+ }
@@ -3,10 +3,27 @@ require 'fileutils'
3
3
  namespace :basecoat do
4
4
  desc "Install Basecoat application layout and partials"
5
5
  task :install do
6
- # Install basecoat-css (always install via yarn/npm for CSS)
6
+ # Install basecoat-css (detect package manager)
7
7
  puts "\n📦 Installing basecoat-css..."
8
- system("yarn add basecoat-css") || system("npm install basecoat-css")
9
- puts " Installed: basecoat-css via yarn/npm"
8
+
9
+ # Detect package manager
10
+ if File.exist?(Rails.root.join("bun.lockb"))
11
+ system("bun add basecoat-css")
12
+ puts " Installed: basecoat-css via bun"
13
+ elsif File.exist?(Rails.root.join("yarn.lock"))
14
+ system("yarn add basecoat-css")
15
+ puts " Installed: basecoat-css via yarn"
16
+ elsif File.exist?(Rails.root.join("package-lock.json"))
17
+ system("npm install basecoat-css")
18
+ puts " Installed: basecoat-css via npm"
19
+ elsif File.exist?(Rails.root.join("pnpm-lock.yaml"))
20
+ system("pnpm add basecoat-css")
21
+ puts " Installed: basecoat-css via pnpm"
22
+ else
23
+ # Fallback: try bun first, then yarn, then npm
24
+ system("bun add basecoat-css") || system("yarn add basecoat-css") || system("npm install basecoat-css")
25
+ puts " Installed: basecoat-css"
26
+ end
10
27
 
11
28
  # If using importmap, also add to importmap.rb for JS
12
29
  if File.exist?(Rails.root.join("config/importmap.rb"))
@@ -41,21 +58,38 @@ namespace :basecoat do
41
58
  puts " Added: basecoat-css import to app/javascript/application.js"
42
59
  end
43
60
 
44
- # Copy basecoat-helper.js
45
- helper_source = File.expand_path("../generators/basecoat/templates/basecoat-helper.js", __dir__)
46
- helper_destination = Rails.root.join("app/javascript/basecoat-helper.js")
47
-
48
- FileUtils.cp(helper_source, helper_destination)
49
- puts " Created: app/javascript/basecoat-helper.js"
50
-
51
- # Add basecoat-helper import
61
+ # Add basecoat helper JavaScript directly to application.js
52
62
  js_content = File.read(js_path)
53
- unless js_content.include?("basecoat-helper")
54
- # Add import after the last import line
55
- js_content = js_content.sub(/(import\s+.*\n)(?!import)/, "\\1import \"basecoat-helper\"\n")
56
- File.write(js_path, js_content)
57
- puts " Added: basecoat-helper import to app/javascript/application.js"
63
+ unless js_content.include?("Re-initialize basecoat-css components")
64
+ basecoat_js = <<~JS
65
+
66
+ // Re-initialize basecoat-css components after Turbo navigation
67
+ document.addEventListener('turbo:load', () => {
68
+ document.dispatchEvent(new Event('DOMContentLoaded', { bubbles: true, cancelable: false }))
69
+ })
70
+
71
+ // View transitions for turbo frame navigation
72
+ addEventListener("turbo:before-frame-render", (event) => {
73
+ if (document.startViewTransition) {
74
+ const originalRender = event.detail.render
75
+ event.detail.render = async (currentElement, newElement) => {
76
+ const transition = document.startViewTransition(() => originalRender(currentElement, newElement))
77
+ await transition.finished
78
+ }
79
+ }
80
+ })
81
+ JS
82
+ File.open(js_path, "a") { |f| f.write(basecoat_js) }
83
+ puts " Added: Basecoat helper JavaScript to app/javascript/application.js"
58
84
  end
85
+
86
+ # Copy theme_controller.js
87
+ theme_controller_source = File.expand_path("../generators/basecoat/templates/theme_controller.js", __dir__)
88
+ theme_controller_destination = Rails.root.join("app/javascript/controllers/theme_controller.js")
89
+
90
+ FileUtils.mkdir_p(File.dirname(theme_controller_destination))
91
+ FileUtils.cp(theme_controller_source, theme_controller_destination)
92
+ puts " Created: app/javascript/controllers/theme_controller.js"
59
93
  end
60
94
 
61
95
  # Add CSS imports and styles
@@ -71,40 +105,76 @@ namespace :basecoat do
71
105
  File.write(tailwind_css, updated_content)
72
106
  puts " Added: basecoat-css import to app/assets/tailwind/application.css"
73
107
  end
74
- else
75
- # Traditional setup with app/assets/stylesheets
76
- css_path = Rails.root.join("app/assets/stylesheets/application.css")
77
- if File.exist?(css_path)
78
- css_content = File.read(css_path)
79
-
80
- css_code = <<~CSS
81
- .field_with_errors label {
82
- color: var(--color-destructive);
83
- }
84
-
85
- .field_with_errors input {
86
- border-color: var(--color-destructive);
87
- }
88
- CSS
89
- File.open(css_path, "a") { |f| f.write(css_code) }
90
- puts " Added: invalid input styles to app/assets/stylesheets/application.css"
108
+ end
109
+ # Traditional setup with app/assets/stylesheets
110
+ # Check for application.tailwind.css first, then application.css
111
+ css_path = if File.exist?(Rails.root.join("app/assets/stylesheets/application.tailwind.css"))
112
+ Rails.root.join("app/assets/stylesheets/application.tailwind.css")
113
+ else
114
+ Rails.root.join("app/assets/stylesheets/application.css")
115
+ end
116
+
117
+ if File.exist?(css_path)
118
+ css_content = File.read(css_path)
119
+
120
+ css_code = <<~CSS
121
+ .field_with_errors {
122
+ label {
123
+ color: var(--color-destructive);
124
+ }
125
+
126
+ input {
127
+ border-color: var(--color-destructive);
128
+ }
129
+ }
130
+
131
+ dl {
132
+ font-size: var(--text-sm);
133
+ dt {
134
+ font-weight: var(--font-weight-bold);
135
+ margin-top: calc(var(--spacing)*4);
136
+ }
137
+ }
138
+ CSS
139
+ File.open(css_path, "a") { |f| f.write(css_code) }
140
+ puts " Added: basic styles to #{css_path.relative_path_from(Rails.root)}"
141
+ end
142
+
143
+ # Extract <head> from existing application.html.erb BEFORE overwriting it
144
+ layout_destination = Rails.root.join("app/views/layouts/application.html.erb")
145
+ partials_source = File.expand_path("../generators/basecoat/templates/layouts", __dir__)
146
+ partials_destination = Rails.root.join("app/views/layouts")
147
+
148
+ FileUtils.mkdir_p(partials_destination)
149
+
150
+ if File.exist?(layout_destination)
151
+ content = File.read(layout_destination)
152
+ # Extract everything between <head> and </head>
153
+ if content =~ /(<head>.*?<\/head>)/m
154
+ head_content = $1
155
+ head_destination = partials_destination.join("_head.html.erb")
156
+ File.write(head_destination, head_content + "\n")
157
+ puts " Created: app/views/layouts/_head.html.erb (extracted from existing application.html.erb)"
158
+ else
159
+ # Fallback: copy the template if no <head> found in existing layout
160
+ FileUtils.cp("#{partials_source}/_head.html.erb", partials_destination.join("_head.html.erb"))
161
+ puts " Created: app/views/layouts/_head.html.erb (from template)"
91
162
  end
163
+ else
164
+ # No existing layout, use template
165
+ FileUtils.cp("#{partials_source}/_head.html.erb", partials_destination.join("_head.html.erb"))
166
+ puts " Created: app/views/layouts/_head.html.erb (from template)"
92
167
  end
93
168
 
94
169
  # Copy application layout
95
170
  layout_source = File.expand_path("../generators/basecoat/templates/application.html.erb", __dir__)
96
- layout_destination = Rails.root.join("app/views/layouts/application.html.erb")
97
-
98
- FileUtils.mkdir_p(File.dirname(layout_destination))
99
171
  FileUtils.cp(layout_source, layout_destination)
100
172
  puts " Created: app/views/layouts/application.html.erb"
101
173
 
102
- # Copy layout partials
103
- partials_source = File.expand_path("../generators/basecoat/templates/layouts", __dir__)
104
- partials_destination = Rails.root.join("app/views/layouts")
105
-
174
+ # Copy layout partials (except _head.html.erb which we already handled)
106
175
  Dir.glob("#{partials_source}/*").each do |file|
107
176
  filename = File.basename(file)
177
+ next if filename == "_head.html.erb" # Skip _head.html.erb, we already created it
108
178
  FileUtils.cp(file, partials_destination.join(filename))
109
179
  puts " Created: app/views/layouts/#{filename}"
110
180
  end
@@ -1,6 +1,5 @@
1
1
  <%%= turbo_frame_tag "main_content" do %>
2
- <%%= render 'layouts/notice', notice: notice if notice %>
3
- <div class="container mx-auto px-4 py-8">
2
+ <%%= render 'layouts/notice', notice: notice if notice %>
4
3
  <div class="max-w-2xl mx-auto">
5
4
  <div class="card">
6
5
  <header>
@@ -17,5 +16,4 @@
17
16
  <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn-outline", data: { turbo_action: "advance" } %>
18
17
  </div>
19
18
  </div>
20
- </div>
21
19
  <%% end %>
@@ -1,22 +1,47 @@
1
1
  <%%= turbo_frame_tag "main_content" do %>
2
- <%%= render 'layouts/notice', notice: notice if notice %>
3
- <div class="container mx-auto px-4 py-8">
2
+ <%%= render 'layouts/notice', notice: notice if notice %>
4
3
  <div class="flex justify-between items-center mb-8">
5
4
  <h1 class="text-3xl"><%= human_name.pluralize %></h1>
6
5
  <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %>, class: "btn", data: { turbo_action: "advance" } %>
7
6
  </div>
8
7
 
9
- <div class="grid gap-4" id="<%= plural_table_name %>">
10
- <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
11
- <div class="card">
12
- <section>
13
- <%%= render <%= singular_table_name %> %>
14
- </section>
15
- <footer class="flex items-center">
16
- <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(singular_table_name) %>, class: "btn-outline", data: { turbo_action: "advance" } %>
17
- </footer>
18
- </div>
19
- <%% end %>
8
+ <div class="overflow-x-auto" id="<%= plural_table_name %>">
9
+ <table class="table">
10
+ <thead>
11
+ <tr class="hover:bg-background">
12
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
13
+ <th>
14
+ <%= attribute.human_name %>
15
+ </th>
16
+ <% end -%>
17
+ <th class="bg-background sticky right-0">
18
+ Actions
19
+ </th>
20
+ </tr>
21
+ </thead>
22
+ <tbody>
23
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
24
+ <tr class="hover:bg-background">
25
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
26
+ <td>
27
+ <% if attribute.attachment? -%>
28
+ <%%= link_to <%= singular_table_name %>.<%= attribute.column_name %>.filename, <%= singular_table_name %>.<%= attribute.column_name %>, target: :_blank, class: "text-blue-600 hover:text-blue-800 underline" if <%= singular_table_name %>.<%= attribute.column_name %>.attached? %>
29
+ <% elsif attribute.attachments? -%>
30
+ <%% <%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
31
+ <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %>, target: :_blank, class: "text-blue-600 hover:text-blue-800 underline" %></div>
32
+ <%% end %>
33
+ <% else -%>
34
+ <%%= <%= singular_table_name %>.<%= attribute.column_name %> %>
35
+ <% end -%>
36
+ </td>
37
+ <% end -%>
38
+ <td class="bg-background sticky right-0">
39
+ <%%= link_to "Show", <%= model_resource_name(singular_table_name) %>, class: "btn-outline", data: { turbo_action: "advance" } %>
40
+ </td>
41
+ </tr>
42
+ <%% end %>
43
+ </tbody>
44
+ </table>
20
45
  </div>
21
46
 
22
47
  <%% if @<%= plural_table_name %>.empty? %>
@@ -31,5 +56,4 @@
31
56
  <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %>, class: "btn", data: { turbo_action: "advance" } %>
32
57
  </div>
33
58
  <%% end %>
34
- </div>
35
59
  <%% end %>
@@ -1,6 +1,5 @@
1
1
  <%%= turbo_frame_tag "main_content" do %>
2
- <%%= render 'layouts/notice', notice: notice if notice %>
3
- <div class="container mx-auto px-4 py-8">
2
+ <%%= render 'layouts/notice', notice: notice if notice %>
4
3
  <div class="max-w-2xl mx-auto">
5
4
  <div class="card">
6
5
  <header>
@@ -16,5 +15,4 @@
16
15
  <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn-outline", data: { turbo_action: "advance" } %>
17
16
  </div>
18
17
  </div>
19
- </div>
20
18
  <%% end %>
@@ -1,23 +1,40 @@
1
1
  <%%= turbo_frame_tag "main_content" do %>
2
2
  <%%= render 'layouts/notice', notice: notice if notice %>
3
- <div class="container mx-auto px-4 py-8">
4
- <div class="max-w-2xl mx-auto">
5
- <div class="card">
6
- <section>
7
- <%%= render @<%= singular_table_name %> %>
8
- </section>
9
- </div>
3
+ <div class="max-w-2xl mx-auto">
4
+ <div class="card">
5
+ <section>
6
+ <div id="<%%= dom_id @<%= singular_table_name %> %>">
7
+ <dl>
8
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
9
+ <dt>
10
+ <%= attribute.human_name %>:
11
+ </dt>
12
+ <dd>
13
+ <% if attribute.attachment? -%>
14
+ <%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %>, target: :_blank if @<%= singular_table_name %>.<%= attribute.column_name %>.attached? %>
15
+ <% elsif attribute.attachments? -%>
16
+ <%% @<%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
17
+ <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %>, target: :_blank %></div>
18
+ <%% end %>
19
+ <% else -%>
20
+ <%%= @<%= singular_table_name %>.<%= attribute.column_name %> %>
21
+ <% end -%>
22
+ </dd>
23
+ <% end -%>
24
+ </dl>
25
+ </div>
26
+ </section>
27
+ </div>
10
28
 
11
- <div class="flex flex-wrap gap-3 mt-6">
12
- <%%= link_to "Edit this <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, class: "btn", data: { turbo_action: "advance" } %>
13
- <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn-outline", data: { turbo_action: "advance" } %>
14
- <%%= button_to "Delete this <%= human_name.downcase %>",
15
- <%= model_resource_name(prefix: "@") %>,
16
- method: :delete,
17
- class: "btn-destructive",
18
- data: { turbo_confirm: "Are you sure you want to delete this <%= human_name.downcase %>?", turbo_action: "advance" },
19
- form_class: "inline-block" %>
20
- </div>
29
+ <div class="flex flex-wrap gap-3 mt-6">
30
+ <%%= link_to "Edit this <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, class: "btn", data: { turbo_action: "advance" } %>
31
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, class: "btn-outline", data: { turbo_action: "advance" } %>
32
+ <%%= button_to "Delete this <%= human_name.downcase %>",
33
+ <%= model_resource_name(prefix: "@") %>,
34
+ method: :delete,
35
+ class: "btn-destructive",
36
+ data: { turbo_confirm: "Are you sure you want to delete this <%= human_name.downcase %>?", turbo_action: "advance" },
37
+ form_class: "inline-block" %>
21
38
  </div>
22
39
  </div>
23
40
  <%% end %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basecoat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martijn Lafeber
@@ -33,6 +33,7 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - ".idea/.gitignore"
35
35
  - ".idea/basecoat.iml"
36
+ - ".idea/codeStyles/codeStyleConfig.xml"
36
37
  - ".idea/misc.xml"
37
38
  - ".idea/modules.xml"
38
39
  - ".idea/vcs.xml"
@@ -43,7 +44,6 @@ files:
43
44
  - lib/basecoat/railtie.rb
44
45
  - lib/basecoat/version.rb
45
46
  - lib/generators/basecoat/templates/application.html.erb
46
- - lib/generators/basecoat/templates/basecoat-helper.js
47
47
  - lib/generators/basecoat/templates/devise.html.erb
48
48
  - lib/generators/basecoat/templates/devise/confirmations/new.html.erb
49
49
  - lib/generators/basecoat/templates/devise/mailer/confirmation_instructions.html.erb
@@ -65,18 +65,19 @@ files:
65
65
  - lib/generators/basecoat/templates/layouts/_head.html.erb
66
66
  - lib/generators/basecoat/templates/layouts/_header.html.erb
67
67
  - lib/generators/basecoat/templates/layouts/_notice.html.erb
68
+ - lib/generators/basecoat/templates/layouts/_theme_toggle.html.erb
68
69
  - lib/generators/basecoat/templates/pagy.scss
69
70
  - lib/generators/basecoat/templates/passwords/edit.html.erb
70
71
  - lib/generators/basecoat/templates/passwords/new.html.erb
71
72
  - lib/generators/basecoat/templates/scaffold_hook.rb
72
73
  - lib/generators/basecoat/templates/sessions.html.erb
73
74
  - lib/generators/basecoat/templates/sessions/new.html.erb
75
+ - lib/generators/basecoat/templates/theme_controller.js
74
76
  - lib/tasks/basecoat.rake
75
77
  - lib/templates/erb/scaffold/_form.html.erb.tt
76
78
  - lib/templates/erb/scaffold/edit.html.erb.tt
77
79
  - lib/templates/erb/scaffold/index.html.erb.tt
78
80
  - lib/templates/erb/scaffold/new.html.erb.tt
79
- - lib/templates/erb/scaffold/partial.html.erb.tt
80
81
  - lib/templates/erb/scaffold/show.html.erb.tt
81
82
  - sig/basecoat.rbs
82
83
  homepage: https://github.com/lafeber/basecoat-rb
@@ -1,38 +0,0 @@
1
- // Re-initialize basecoat-css components after Turbo navigation
2
- document.addEventListener('turbo:load', () => {
3
- document.dispatchEvent(new Event('DOMContentLoaded', { bubbles: true, cancelable: false }))
4
- })
5
-
6
- // View transitions for turbo frame navigation
7
- addEventListener("turbo:before-frame-render", (event) => {
8
- if (document.startViewTransition) {
9
- const originalRender = event.detail.render
10
- event.detail.render = async (currentElement, newElement) => {
11
- const transition = document.startViewTransition(() => originalRender(currentElement, newElement))
12
- await transition.finished
13
- }
14
- }
15
- })
16
-
17
- // Dark mode toggle
18
- const apply = dark => {
19
- document.documentElement.classList.toggle('dark', dark);
20
- try { localStorage.setItem('themeMode', dark ? 'dark' : 'light'); } catch (_) {}
21
- };
22
-
23
- // Apply theme on initial load (runs immediately to prevent flash)
24
- try {
25
- const stored = localStorage.getItem('themeMode');
26
- if (stored ? stored === 'dark'
27
- : matchMedia('(prefers-color-scheme: dark)').matches) {
28
- document.documentElement.classList.add('dark');
29
- }
30
- } catch (_) {}
31
-
32
- // Set up theme toggle event listener
33
- document.addEventListener('basecoat:theme', (event) => {
34
- const mode = event.detail?.mode;
35
- apply(mode === 'dark' ? true
36
- : mode === 'light' ? false
37
- : !document.documentElement.classList.contains('dark'));
38
- })
@@ -1,20 +0,0 @@
1
- <div id="<%%= dom_id <%= singular_table_name %> %>" class="space-y-4">
2
- <% attributes.reject(&:password_digest?).each do |attribute| -%>
3
- <div class="flex flex-col sm:flex-row sm:items-center py-2">
4
- <dt class="text-sm font-medium sm:w-1/3 mb-1 sm:mb-0">
5
- <%= attribute.human_name %>:
6
- </dt>
7
- <dd class="text-sm sm:w-2/3">
8
- <% if attribute.attachment? -%>
9
- <%%= link_to <%= singular_table_name %>.<%= attribute.column_name %>.filename, <%= singular_table_name %>.<%= attribute.column_name %>, target: :_blank, class: "text-blue-600 hover:text-blue-800 underline" if <%= singular_table_name %>.<%= attribute.column_name %>.attached? %>
10
- <% elsif attribute.attachments? -%>
11
- <%% <%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
12
- <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %>, target: :_blank, class: "text-blue-600 hover:text-blue-800 underline" %></div>
13
- <%% end %>
14
- <% else -%>
15
- <%%= <%= singular_table_name %>.<%= attribute.column_name %> %>
16
- <% end -%>
17
- </dd>
18
- </div>
19
- <% end -%>
20
- </div>