seams 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +212 -12
- data/README.md +235 -82
- data/lib/generators/seams/accounts/accounts_generator.rb +31 -3
- data/lib/generators/seams/accounts/templates/app/controllers/memberships_controller.rb.tt +100 -0
- data/lib/generators/seams/accounts/templates/app/views/memberships/index.html.erb.tt +52 -0
- data/lib/generators/seams/accounts/templates/config/routes.rb.tt +10 -4
- data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +35 -2
- data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +13 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/authorization_spec.rb.tt +102 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/memberships_flow_spec.rb.tt +145 -0
- data/lib/generators/seams/admin/admin_generator.rb +51 -4
- data/lib/generators/seams/admin/templates/README.md.tt +33 -3
- data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +156 -8
- data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +9 -1
- data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +9 -1
- data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +7 -1
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +12 -2
- data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +7 -1
- data/lib/generators/seams/admin/templates/app/fields/admin/fields/belongs_to.rb.tt +12 -0
- data/lib/generators/seams/admin/templates/app/fields/admin/fields/dashboard_option.rb.tt +22 -0
- data/lib/generators/seams/admin/templates/app/fields/admin/fields/has_many.rb.tt +12 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +4 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +51 -10
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +4 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +7 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +6 -0
- data/lib/generators/seams/admin/templates/app/views/seams/admin/application/_index_header.html.erb.tt +57 -0
- data/lib/generators/seams/admin/templates/app/views/seams/admin/application/_navigation.html.erb.tt +14 -0
- data/lib/generators/seams/admin/templates/config/routes.rb.tt +24 -14
- data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +3 -0
- data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +3 -1
- data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +4 -2
- data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +1 -2
- data/lib/generators/seams/auth/auth_generator.rb +1 -2
- data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +3 -0
- data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +4 -0
- data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +5 -0
- data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +5 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +5 -1
- data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +12 -1
- data/lib/generators/seams/auth/templates/lib/engine.rb.tt +27 -0
- data/lib/generators/seams/billing/billing_generator.rb +4 -3
- data/lib/generators/seams/billing/templates/README.md.tt +26 -0
- data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +2 -0
- data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +2 -0
- data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +8 -7
- data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +9 -7
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +18 -3
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +7 -21
- data/lib/generators/seams/billing/templates/lib/engine.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +10 -10
- data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +40 -14
- data/lib/generators/seams/billing/templates/lib/stripe/payload.rb.tt +90 -0
- data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +11 -1
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +1 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +1 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +15 -4
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +7 -1
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +18 -1
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +26 -5
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +16 -2
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +16 -2
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +17 -2
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +16 -2
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +16 -2
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +1 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +1 -0
- data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +47 -1
- data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +39 -1
- data/lib/generators/seams/design/design_generator.rb +647 -0
- data/lib/generators/seams/design/templates/README.md.tt +129 -0
- data/lib/generators/seams/design/templates/app/assets/tailwind/_tokens.css +322 -0
- data/lib/generators/seams/design/templates/app/assets/tailwind/themes/_quire.css +42 -0
- data/lib/generators/seams/design/templates/app/controllers/design/dashboard_controller.rb.tt +30 -0
- data/lib/generators/seams/design/templates/app/controllers/design/guide_controller.rb.tt +28 -0
- data/lib/generators/seams/design/templates/app/form_builders/design/form_builder.rb.tt +90 -0
- data/lib/generators/seams/design/templates/app/helpers/design/ui_helper.rb.tt +37 -0
- data/lib/generators/seams/design/templates/app/views/design/dashboard/index.html.erb.tt +38 -0
- data/lib/generators/seams/design/templates/app/views/design/guide/index.html.erb.tt +19 -0
- data/lib/generators/seams/design/templates/app/views/layouts/application.html.erb.tt +61 -0
- data/lib/generators/seams/design/templates/app/views/layouts/design/guide.html.erb.tt +21 -0
- data/lib/generators/seams/design/templates/app/views/ui/_banner.html.erb.tt +10 -0
- data/lib/generators/seams/design/templates/app/views/ui/_breadcrumb.html.erb.tt +8 -0
- data/lib/generators/seams/design/templates/app/views/ui/_build_row.html.erb.tt +14 -0
- data/lib/generators/seams/design/templates/app/views/ui/_button.html.erb.tt +4 -0
- data/lib/generators/seams/design/templates/app/views/ui/_card.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_chapter_row.html.erb.tt +11 -0
- data/lib/generators/seams/design/templates/app/views/ui/_checkbox.html.erb.tt +12 -0
- data/lib/generators/seams/design/templates/app/views/ui/_counter.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_data_table.html.erb.tt +25 -0
- data/lib/generators/seams/design/templates/app/views/ui/_dialog.html.erb.tt +14 -0
- data/lib/generators/seams/design/templates/app/views/ui/_diff.html.erb.tt +12 -0
- data/lib/generators/seams/design/templates/app/views/ui/_drawer.html.erb.tt +7 -0
- data/lib/generators/seams/design/templates/app/views/ui/_empty.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/_field.html.erb.tt +14 -0
- data/lib/generators/seams/design/templates/app/views/ui/_icon.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_icon_sprite.html.erb.tt +22 -0
- data/lib/generators/seams/design/templates/app/views/ui/_input_group.html.erb.tt +11 -0
- data/lib/generators/seams/design/templates/app/views/ui/_kbd.html.erb.tt +4 -0
- data/lib/generators/seams/design/templates/app/views/ui/_menu.html.erb.tt +18 -0
- data/lib/generators/seams/design/templates/app/views/ui/_meter.html.erb.tt +15 -0
- data/lib/generators/seams/design/templates/app/views/ui/_note.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_outline.html.erb.tt +9 -0
- data/lib/generators/seams/design/templates/app/views/ui/_pagination.html.erb.tt +12 -0
- data/lib/generators/seams/design/templates/app/views/ui/_panel.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_popover.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_radio.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/_savestate.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/_segmented.html.erb.tt +7 -0
- data/lib/generators/seams/design/templates/app/views/ui/_stepper.html.erb.tt +12 -0
- data/lib/generators/seams/design/templates/app/views/ui/_switch.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/_tag.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/_toast.html.erb.tt +12 -0
- data/lib/generators/seams/design/templates/app/views/ui/_toolbar.html.erb.tt +15 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_banner.html.erb.tt +8 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_breadcrumb.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_build_row.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_button.html.erb.tt +9 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_card.html.erb.tt +12 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_chapter_row.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_checkbox.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_counter.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_data_table.html.erb.tt +14 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_dialog.html.erb.tt +8 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_diff.html.erb.tt +10 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_drawer.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_empty.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_field.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_input_group.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_kbd.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_menu.html.erb.tt +9 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_meter.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_note.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_outline.html.erb.tt +8 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_pagination.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_panel.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_popover.html.erb.tt +2 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_radio.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_savestate.html.erb.tt +4 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_segmented.html.erb.tt +15 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_stepper.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_switch.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_tag.html.erb.tt +8 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_toast.html.erb.tt +6 -0
- data/lib/generators/seams/design/templates/app/views/ui/previews/_toolbar.html.erb.tt +11 -0
- data/lib/generators/seams/design/templates/lib/design/components.rb.tt +22 -0
- data/lib/generators/seams/design/templates/lib/design.rb.tt +18 -0
- data/lib/generators/seams/design/templates/lib/engine.rb.tt +38 -0
- data/lib/generators/seams/design/templates/lib/generators/design/component/component_generator.rb.tt +52 -0
- data/lib/generators/seams/design/templates/lib/generators/design/component/templates/component.html.erb.tt +5 -0
- data/lib/generators/seams/design/templates/lib/generators/design/component/templates/preview.html.erb.tt +3 -0
- data/lib/generators/seams/design/templates/spec/runtime/design_boot_spec.rb.tt +79 -0
- data/lib/generators/seams/design/templates/spec/runtime/form_builder_spec.rb.tt +97 -0
- data/lib/generators/seams/design/templates/spec/runtime/guide_spec.rb.tt +40 -0
- data/lib/generators/seams/design/templates/spec/runtime/ui_components_spec.rb.tt +70 -0
- data/lib/generators/seams/engine/engine_generator.rb +6 -0
- data/lib/generators/seams/engine/templates/Gemfile.tt +1 -1
- data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
- data/lib/generators/seams/engine/templates/rubocop.yml.tt +9 -0
- data/lib/generators/seams/install/install_generator.rb +72 -6
- data/lib/generators/seams/install/templates/Dockerfile.tt +2 -2
- data/lib/generators/seams/install/templates/bin_seams.tt +7 -4
- data/lib/generators/seams/install/templates/ci.yml.tt +18 -8
- data/lib/generators/seams/install/templates/deploy.yml.tt +4 -2
- data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +1 -1
- data/lib/generators/seams/install/templates/docker-entrypoint.tt +4 -2
- data/lib/generators/seams/install/templates/herb.yml.tt +10 -0
- data/lib/generators/seams/install/templates/lefthook.yml.tt +25 -0
- data/lib/generators/seams/install/templates/ruby-version.tt +1 -1
- data/lib/generators/seams/install/templates/seams.rake.tt +13 -1
- data/lib/generators/seams/install/templates/strong_migrations.rb.tt +26 -0
- data/lib/generators/seams/notifications/notifications_generator.rb +3 -2
- data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +5 -13
- data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +12 -0
- data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +1 -0
- data/lib/generators/seams/notifications/templates/lib/preferences.rb.tt +36 -0
- data/lib/generators/seams/permissions/permissions_generator.rb +97 -0
- data/lib/generators/seams/permissions/templates/config/initializers/seams_permissions.rb.tt +35 -0
- data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +27 -11
- data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +5 -13
- data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +2 -4
- data/lib/generators/seams/teams/templates/lib/engine.rb.tt +13 -0
- data/lib/seams/cli/list.rb +15 -0
- data/lib/seams/cli/quality.rb +46 -5
- data/lib/seams/cli/resolve.rb +9 -5
- data/lib/seams/cli/test_changed.rb +5 -1
- data/lib/seams/cli.rb +20 -0
- data/lib/seams/configuration.rb +24 -1
- data/lib/seams/cops/no_cross_engine_dependency.rb +39 -7
- data/lib/seams/cops/no_cross_engine_model_access.rb +79 -1
- data/lib/seams/event_registry.rb +19 -0
- data/lib/seams/events/adapter.rb +9 -0
- data/lib/seams/events/adapters/active_support.rb +4 -0
- data/lib/seams/events/publisher.rb +29 -0
- data/lib/seams/events.rb +1 -0
- data/lib/seams/generators/dummy_app_writer.rb +21 -0
- data/lib/seams/generators/follow_up_generator.rb +1 -2
- data/lib/seams/generators/host_injector.rb +9 -2
- data/lib/seams/generators/splicer.rb +2 -2
- data/lib/seams/observability/adapter.rb +22 -0
- data/lib/seams/observability.rb +6 -0
- data/lib/seams/permission_registry.rb +66 -0
- data/lib/seams/permissions.rb +120 -0
- data/lib/seams/version.rb +1 -1
- data/lib/seams.rb +7 -0
- metadata +107 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Design
|
|
4
|
+
# The host-wide design-system helper. The only hand-written helper is the one
|
|
5
|
+
# that isn't a plain "render a component" — currently just ui_icon. Every
|
|
6
|
+
# public component (one with a preview partial) gets a `ui_<name>` helper
|
|
7
|
+
# defined automatically (see define_component_helpers!), so adding a component
|
|
8
|
+
# never means adding a helper.
|
|
9
|
+
#
|
|
10
|
+
# Mixed into ActionController::Base by the engine's to_prepare hook, so the
|
|
11
|
+
# helpers resolve in the host's views AND every other engine's views.
|
|
12
|
+
module UiHelper
|
|
13
|
+
# <%%= ui_icon(:close) %> — references the inline sprite by fragment.
|
|
14
|
+
def ui_icon(name, size: nil)
|
|
15
|
+
return if name.nil?
|
|
16
|
+
|
|
17
|
+
render "ui/icon", name: name, size: size
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Define ui_<name>(**locals, &block) for every public component. The block,
|
|
21
|
+
# if given, is captured and passed as the `content` local — so a component
|
|
22
|
+
# that takes content declares `content: nil` in its strict locals, and every
|
|
23
|
+
# other keyword flows straight through (extras land in the partial's trailing
|
|
24
|
+
# **attrs and are splatted onto its root element).
|
|
25
|
+
def self.define_component_helpers!
|
|
26
|
+
Design.component_names.each do |name|
|
|
27
|
+
method_name = "ui_#{name}"
|
|
28
|
+
next if instance_methods(false).include?(method_name.to_sym)
|
|
29
|
+
|
|
30
|
+
define_method(method_name) do |**locals, &block|
|
|
31
|
+
locals = locals.merge(content: capture(&block)) if block
|
|
32
|
+
render "ui/#{name}", **locals
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<%% content_for :title, "Dashboard — <%= app_name %>" %>
|
|
2
|
+
|
|
3
|
+
<div class="stack-lg">
|
|
4
|
+
<header class="stack">
|
|
5
|
+
<p class="eyebrow">Dashboard</p>
|
|
6
|
+
<h1 class="display">Welcome to <%= app_name %></h1>
|
|
7
|
+
<p class="lede">
|
|
8
|
+
This starter dashboard ships with <code>bin/seams design --shell</code>. It is
|
|
9
|
+
built entirely from <code>ui_*</code> components on the seams design system —
|
|
10
|
+
replace it with your real home page when you have data to show.
|
|
11
|
+
</p>
|
|
12
|
+
<div class="row-wrap">
|
|
13
|
+
<%%= ui_button(variant: :accent) { "Primary action" } %>
|
|
14
|
+
<%%= link_to "Component gallery", "/design/guide", class: "btn btn-ghost" if Rails.env.local? %>
|
|
15
|
+
</div>
|
|
16
|
+
</header>
|
|
17
|
+
|
|
18
|
+
<section class="stack">
|
|
19
|
+
<h2 class="h2">Add an engine</h2>
|
|
20
|
+
<p class="small muted">
|
|
21
|
+
A fresh host has no feature engines yet. Generate one with <code>bin/seams <name></code>
|
|
22
|
+
and its styled views render on top of this design system.
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<div class="grid grid-auto">
|
|
26
|
+
<%% @suggested_engines.each do |name, summary| %>
|
|
27
|
+
<%%= ui_card do %>
|
|
28
|
+
<div class="row">
|
|
29
|
+
<span class="mono small muted">bin/seams <%%= name %></span>
|
|
30
|
+
<%%= ui_tag(label: "available", status: :neutral) %>
|
|
31
|
+
</div>
|
|
32
|
+
<h3 class="h3" style="margin-top:var(--s-2)"><%%= name.capitalize %></h3>
|
|
33
|
+
<p class="small muted" style="margin-top:var(--s-1)"><%%= summary %></p>
|
|
34
|
+
<%% end %>
|
|
35
|
+
<%% end %>
|
|
36
|
+
</div>
|
|
37
|
+
</section>
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%% content_for :title, "Design — component guide" %>
|
|
2
|
+
<main id="main" style="max-width:var(--shell-max);margin:0 auto;padding:var(--s-7) var(--s-5) var(--s-9);font-family:var(--sans);color:var(--ink)">
|
|
3
|
+
<p style="font-size:var(--t-cap);letter-spacing:.08em;text-transform:uppercase;color:var(--muted)">Design · living gallery</p>
|
|
4
|
+
<h1 style="font-family:var(--display);font-size:var(--t-display);margin:var(--s-3) 0 0">Components, rendered from the real partials</h1>
|
|
5
|
+
<p style="font-size:var(--t-body);color:var(--ink-2);max-width:var(--measure);margin:var(--s-4) 0 0">Every example below is the real <code><%%= "ui_*" %></code> helper rendering its
|
|
6
|
+
strict-locals partial. This page is built from the components themselves — add one (a partial + a preview under
|
|
7
|
+
<code>app/views/ui/</code>) and it appears here, with its helper, automatically.</p>
|
|
8
|
+
<p style="font-size:var(--t-sm);color:var(--muted);margin:var(--s-4) 0 0"><%%= @components.size %> components</p>
|
|
9
|
+
|
|
10
|
+
<div style="display:flex;flex-direction:column;gap:var(--s-7);margin-top:var(--s-7)">
|
|
11
|
+
<%% @components.each do |name| %>
|
|
12
|
+
<section>
|
|
13
|
+
<p style="font-size:var(--t-cap);letter-spacing:.04em;color:var(--muted);font-family:var(--mono)">ui_<%%= name %></p>
|
|
14
|
+
<h2 style="font-size:var(--t-h2);margin:var(--s-2) 0 var(--s-4)"><%%= name.humanize %></h2>
|
|
15
|
+
<div style="padding:var(--s-5);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--card)"><%%= render "ui/previews/#{name}" %></div>
|
|
16
|
+
</section>
|
|
17
|
+
<%% end %>
|
|
18
|
+
</div>
|
|
19
|
+
</main>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<title><%%= content_for(:title) || "<%= app_name %>" %></title>
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<%%= csrf_meta_tags %>
|
|
7
|
+
<%%= csp_meta_tag %>
|
|
8
|
+
<%%= yield :head %>
|
|
9
|
+
<%%# The "tailwind" build is the compiled entrypoint that carries the
|
|
10
|
+
seams:design @theme tokens + the ui_* component layer. It must be
|
|
11
|
+
linked, or the host boots unstyled. The "application" manifest is
|
|
12
|
+
kept so hosts can add their own non-Tailwind styles. %>
|
|
13
|
+
<%%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
|
|
14
|
+
<%%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
|
15
|
+
<%%= javascript_importmap_tags if respond_to?(:javascript_importmap_tags) %>
|
|
16
|
+
</head>
|
|
17
|
+
|
|
18
|
+
<body style="font-family:var(--sans);color:var(--ink);background:var(--paper);min-height:100vh">
|
|
19
|
+
<a href="#main" class="skip sr-only">Skip to content</a>
|
|
20
|
+
|
|
21
|
+
<%%# seams design — icon sprite for ui_* components %>
|
|
22
|
+
<%%= render "ui/icon_sprite" %>
|
|
23
|
+
|
|
24
|
+
<header class="app-header" role="banner">
|
|
25
|
+
<nav class="app-nav" aria-label="Primary">
|
|
26
|
+
<a href="/" class="app-brand">
|
|
27
|
+
<span class="mark" aria-hidden="true"></span>
|
|
28
|
+
<%= app_name %>
|
|
29
|
+
</a>
|
|
30
|
+
<span class="spacer"></span>
|
|
31
|
+
<%%# A starter account menu, rendered from the ui_menu component. A host
|
|
32
|
+
with auth swaps this for its real sign-in / account links. %>
|
|
33
|
+
<%%= ui_button(variant: :ghost, size: :sm) { "Account" } %>
|
|
34
|
+
</nav>
|
|
35
|
+
</header>
|
|
36
|
+
|
|
37
|
+
<%%# Flash messages, rendered through the banner component's tone variants. %>
|
|
38
|
+
<%% if flash[:notice].present? %>
|
|
39
|
+
<div class="shell" style="margin-top:var(--s-4)">
|
|
40
|
+
<%%= ui_banner(message: flash[:notice], kind: :info, role: "status") %>
|
|
41
|
+
</div>
|
|
42
|
+
<%% end %>
|
|
43
|
+
<%% if flash[:alert].present? %>
|
|
44
|
+
<div class="shell" style="margin-top:var(--s-4)">
|
|
45
|
+
<%%= ui_banner(message: flash[:alert], kind: :alert, role: "alert") %>
|
|
46
|
+
</div>
|
|
47
|
+
<%% end %>
|
|
48
|
+
|
|
49
|
+
<main id="main" class="shell" style="padding-top:var(--s-7);padding-bottom:var(--s-7)">
|
|
50
|
+
<%%= yield %>
|
|
51
|
+
</main>
|
|
52
|
+
|
|
53
|
+
<footer class="app-foot" role="contentinfo">
|
|
54
|
+
<div class="shell row-wrap">
|
|
55
|
+
<span><%= app_name %></span>
|
|
56
|
+
<span class="spacer"></span>
|
|
57
|
+
<span class="small muted">Built with the seams design system</span>
|
|
58
|
+
</div>
|
|
59
|
+
</footer>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title><%%= content_for(:title) || "Design — component guide" %></title>
|
|
7
|
+
<%%= csrf_meta_tags %>
|
|
8
|
+
<%%= csp_meta_tag %>
|
|
9
|
+
<%%# The "tailwind" build is the compiled entrypoint carrying the @theme
|
|
10
|
+
tokens + ui_* component layer; link it or the guide renders unstyled.
|
|
11
|
+
The "application" manifest is kept for the host's own styles. %>
|
|
12
|
+
<%%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" if defined?(stylesheet_link_tag) %>
|
|
13
|
+
<%%= stylesheet_link_tag "application", "data-turbo-track": "reload" if defined?(stylesheet_link_tag) %>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<%%# The design-system icon sprite, so ui_icon and any sprite-referencing
|
|
17
|
+
component resolves on the guide page without the host layout. %>
|
|
18
|
+
<%%= render "ui/icon_sprite" %>
|
|
19
|
+
<%%= yield %>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%%# locals: (message:, kind: :info, content: nil, **attrs) -%>
|
|
2
|
+
<%% icon = kind.to_sym == :alert ? :warning : :info -%>
|
|
3
|
+
<%%= tag.div class: class_names("banner", "banner-#{kind}"), role: "status", **attrs do %>
|
|
4
|
+
<%%= ui_icon(icon) %>
|
|
5
|
+
<%%= tag.span message %>
|
|
6
|
+
<%% if content -%>
|
|
7
|
+
<%%= tag.span class: "spacer" %>
|
|
8
|
+
<%%= content %>
|
|
9
|
+
<%% end -%>
|
|
10
|
+
<%% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%%# locals: (trail:, current:, **attrs) -%>
|
|
2
|
+
<%%= tag.nav class: "crumbs", "aria-label": "Breadcrumb", **attrs do %>
|
|
3
|
+
<%% trail.each do |label, href| -%>
|
|
4
|
+
<%%= tag.a label, href: href %>
|
|
5
|
+
<%%= tag.span "/", class: "sep", "aria-hidden": "true" %>
|
|
6
|
+
<%% end -%>
|
|
7
|
+
<%%= tag.b current, "aria-current": "page" %>
|
|
8
|
+
<%% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%%# locals: (format:, state: :queued, percent: 0, **attrs) -%>
|
|
2
|
+
<%% width = [[percent.to_i, 0].max, 100].min -%>
|
|
3
|
+
<%% status, label = { ready: [:ready, "Ready"], running: [:progress, "Building…"], queued: [:neutral, "Queued"] }.fetch(state, [:neutral, "Queued"]) -%>
|
|
4
|
+
<%%= tag.div class: "buildrow", **attrs do %>
|
|
5
|
+
<%%= tag.span format, class: "fmt" %>
|
|
6
|
+
<%%= tag.span class: "meter" do %>
|
|
7
|
+
<%%= tag.div class: "meter-track", role: "progressbar",
|
|
8
|
+
"aria-valuenow": width, "aria-valuemin": 0, "aria-valuemax": 100,
|
|
9
|
+
"aria-label": "#{format} build progress" do %>
|
|
10
|
+
<%%= tag.div class: class_names("meter-fill", state == :ready ? "at" : nil), style: "width:#{width}%" %>
|
|
11
|
+
<%% end %>
|
|
12
|
+
<%% end %>
|
|
13
|
+
<%%= ui_tag(label: label, status: status) %>
|
|
14
|
+
<%% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%%# locals: (index:, title:, status: :neutral, status_label: "Draft", **attrs) -%>
|
|
2
|
+
<%%= tag.li class: "chapter-row", **attrs do %>
|
|
3
|
+
<%%= tag.span "⠿", class: "drag", "aria-hidden": "true" %>
|
|
4
|
+
<%%= tag.span format("%02d", index), class: "idx" %>
|
|
5
|
+
<%%= tag.span title, class: "title" %>
|
|
6
|
+
<%%= ui_tag(label: status_label, status: status) %>
|
|
7
|
+
<%%= tag.span class: "moves" do %>
|
|
8
|
+
<%%= tag.button "▲", type: "button", "aria-label": "Move up" %>
|
|
9
|
+
<%%= tag.button "▼", type: "button", "aria-label": "Move down" %>
|
|
10
|
+
<%% end %>
|
|
11
|
+
<%% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%%# locals: (label:, name:, value: "1", checked: false, hint: nil, **attrs) -%>
|
|
2
|
+
<%% box_id = "c-#{name.to_s.parameterize}-#{value.to_s.parameterize}" -%>
|
|
3
|
+
<%%= tag.label class: "choice", for: box_id, **attrs do %>
|
|
4
|
+
<%%= tag.input type: "checkbox", name: name, id: box_id, value: value, checked: checked,
|
|
5
|
+
"aria-describedby": (hint ? "#{box_id}-hint" : nil) %>
|
|
6
|
+
<span>
|
|
7
|
+
<%%= tag.span label, class: "ct" %>
|
|
8
|
+
<%% if hint -%>
|
|
9
|
+
<%%= tag.span hint, class: "cd", id: "#{box_id}-hint" %>
|
|
10
|
+
<%% end -%>
|
|
11
|
+
</span>
|
|
12
|
+
<%% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%%# locals: (columns:, rows:, **attrs) -%>
|
|
2
|
+
<%%= tag.div class: "table-wrap", **attrs do %>
|
|
3
|
+
<%%= tag.table class: "table" do %>
|
|
4
|
+
<thead>
|
|
5
|
+
<tr>
|
|
6
|
+
<%% columns.each do |col| -%>
|
|
7
|
+
<%%= tag.th scope: "col",
|
|
8
|
+
class: class_names("sortable" => col[:sortable], "num" => col[:num]),
|
|
9
|
+
"aria-sort": col[:sort] do %>
|
|
10
|
+
<%%= col[:label] %><%% if col[:sortable] %><%%= tag.span "↕", class: "ar", "aria-hidden": "true" %><%% end %>
|
|
11
|
+
<%% end %>
|
|
12
|
+
<%% end -%>
|
|
13
|
+
</tr>
|
|
14
|
+
</thead>
|
|
15
|
+
<tbody>
|
|
16
|
+
<%% rows.each do |row| -%>
|
|
17
|
+
<tr>
|
|
18
|
+
<%% row.each_with_index do |cell, i| -%>
|
|
19
|
+
<%%= tag.td cell, class: class_names("num" => columns[i] && columns[i][:num]) %>
|
|
20
|
+
<%% end -%>
|
|
21
|
+
</tr>
|
|
22
|
+
<%% end -%>
|
|
23
|
+
</tbody>
|
|
24
|
+
<%% end %>
|
|
25
|
+
<%% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%%# locals: (id:, title:, trigger:, destructive: false, content: nil, **attrs) -%>
|
|
2
|
+
<%%= tag.div data: { controller: "ui-dialog" }, **attrs do %>
|
|
3
|
+
<%%= ui_button(variant: (destructive ? :danger : :default), data: { action: "ui-dialog#open" }) { trigger } %>
|
|
4
|
+
<%%= tag.dialog id: id, class: "cdialog", "aria-labelledby": "#{id}-title",
|
|
5
|
+
data: { "ui-dialog-target": "dialog" } do %>
|
|
6
|
+
<div class="dialog">
|
|
7
|
+
<div class="dialog-head">
|
|
8
|
+
<%%= tag.h2 title, id: "#{id}-title" %>
|
|
9
|
+
<button type="button" class="x" aria-label="Close" data-action="ui-dialog#close"><%%= ui_icon(:close) %></button>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="dialog-body"><%%= content %></div>
|
|
12
|
+
</div>
|
|
13
|
+
<%% end %>
|
|
14
|
+
<%% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%%# locals: (file:, lines:, **attrs) -%>
|
|
2
|
+
<%%= tag.div class: "diff", **attrs do %>
|
|
3
|
+
<%%= tag.p file, class: "diff-file" %>
|
|
4
|
+
<%% lines.each do |kind, text| -%>
|
|
5
|
+
<%% sign = kind == :add ? "+" : kind == :del ? "−" : " " -%>
|
|
6
|
+
<%% sign_label = kind == :add ? "added" : kind == :del ? "removed" : nil -%>
|
|
7
|
+
<%%= tag.div class: class_names("diff-row", "diff-#{kind}") do %>
|
|
8
|
+
<%%= tag.span sign, class: "diff-sign", "aria-label": sign_label %>
|
|
9
|
+
<%%= tag.span text, class: "diff-text" %>
|
|
10
|
+
<%% end %>
|
|
11
|
+
<%% end -%>
|
|
12
|
+
<%% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%%# locals: (label:, name:, type: "text", hint: nil, error: nil, value: nil, id: nil, **attrs) -%>
|
|
2
|
+
<%% field_id = id || "f-#{name.to_s.parameterize}" -%>
|
|
3
|
+
<%% described_by = error ? "#{field_id}-error" : (hint ? "#{field_id}-hint" : nil) -%>
|
|
4
|
+
<div class="field<%%= " field-error" if error %>">
|
|
5
|
+
<%%= tag.label label, class: "label", for: field_id %>
|
|
6
|
+
<%%= tag.input class: "input", type: type, name: name, id: field_id, value: value,
|
|
7
|
+
"aria-invalid": (error ? "true" : nil), "aria-describedby": described_by, **attrs %>
|
|
8
|
+
<%% if hint && !error -%>
|
|
9
|
+
<%%= tag.p hint, class: "hint", id: "#{field_id}-hint" %>
|
|
10
|
+
<%% end -%>
|
|
11
|
+
<%% if error -%>
|
|
12
|
+
<%%= tag.p error, class: "hint", id: "#{field_id}-error" %>
|
|
13
|
+
<%% end -%>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%%# locals: () -%>
|
|
2
|
+
<%%# The design-system icon sprite, injected once per page. Components reference
|
|
3
|
+
symbols by fragment (<use href="#ui-icon-NAME">) so they work without an
|
|
4
|
+
external request. Render this once near the top of <body>. -%>
|
|
5
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
|
6
|
+
<symbol id="ui-icon-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><line x1="20" y1="20" x2="15.5" y2="15.5"/></symbol>
|
|
7
|
+
<symbol id="ui-icon-settings" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3.2"/><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M18.4 5.6l-2.1 2.1M7.7 16.3l-2.1 2.1"/></symbol>
|
|
8
|
+
<symbol id="ui-icon-bell" viewBox="0 0 24 24"><path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6z"/><path d="M10 20a2 2 0 0 0 4 0"/></symbol>
|
|
9
|
+
<symbol id="ui-icon-plus" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
|
|
10
|
+
<symbol id="ui-icon-trash" viewBox="0 0 24 24"><polyline points="4 7 20 7"/><path d="M6 7l1 13h10l1-13"/><path d="M9 7V4h6v3"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></symbol>
|
|
11
|
+
<symbol id="ui-icon-chevron-up" viewBox="0 0 24 24"><polyline points="6 15 12 9 18 15"/></symbol>
|
|
12
|
+
<symbol id="ui-icon-chevron-down" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></symbol>
|
|
13
|
+
<symbol id="ui-icon-chevron-right" viewBox="0 0 24 24"><polyline points="9 6 15 12 9 18"/></symbol>
|
|
14
|
+
<symbol id="ui-icon-check" viewBox="0 0 24 24"><polyline points="5 12 10 17 19 7"/></symbol>
|
|
15
|
+
<symbol id="ui-icon-close" viewBox="0 0 24 24"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></symbol>
|
|
16
|
+
<symbol id="ui-icon-warning" viewBox="0 0 24 24"><path d="M12 3l9 16H3z"/><line x1="12" y1="9" x2="12" y2="14"/><circle cx="12" cy="16.8" r="0.6" fill="currentColor" stroke="none"/></symbol>
|
|
17
|
+
<symbol id="ui-icon-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><line x1="12" y1="11" x2="12" y2="16"/><circle cx="12" cy="8" r="0.7" fill="currentColor" stroke="none"/></symbol>
|
|
18
|
+
<symbol id="ui-icon-upload" viewBox="0 0 24 24"><path d="M7 18a4 4 0 0 1-.8-7.9A5 5 0 0 1 16 9a3.5 3.5 0 0 1 1 6.9"/><polyline points="9 13.5 12 10.5 15 13.5"/><line x1="12" y1="10.5" x2="12" y2="19"/></symbol>
|
|
19
|
+
<symbol id="ui-icon-download" viewBox="0 0 24 24"><path d="M4 16v3a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-3"/><polyline points="8 11 12 15 16 11"/><line x1="12" y1="4" x2="12" y2="15"/></symbol>
|
|
20
|
+
<symbol id="ui-icon-edit" viewBox="0 0 24 24"><path d="M4 20l4-1L19 8l-3-3L5 16z"/><line x1="14.5" y1="6.5" x2="17.5" y2="9.5"/></symbol>
|
|
21
|
+
<symbol id="ui-icon-eye" viewBox="0 0 24 24"><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></symbol>
|
|
22
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%%# locals: (name:, value: nil, prefix: nil, suffix: nil, placeholder: nil, **attrs) -%>
|
|
2
|
+
<%% input_id = "ig-#{name.to_s.parameterize}" -%>
|
|
3
|
+
<div class="input-group">
|
|
4
|
+
<%% if prefix -%>
|
|
5
|
+
<%%= tag.span prefix, class: "affix" %>
|
|
6
|
+
<%% end -%>
|
|
7
|
+
<%%= tag.input class: "input", type: "text", name: name, id: input_id, value: value, placeholder: placeholder, **attrs %>
|
|
8
|
+
<%% if suffix -%>
|
|
9
|
+
<%%= tag.span suffix, class: "affix suffix" %>
|
|
10
|
+
<%% end -%>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%%# locals: (items:, label: nil, **attrs) -%>
|
|
2
|
+
<%%= tag.div class: "menu", role: "menu", **attrs do %>
|
|
3
|
+
<%% if label.present? -%>
|
|
4
|
+
<%%= tag.p label, class: "menu-label" %>
|
|
5
|
+
<%% end -%>
|
|
6
|
+
<%% items.each do |item| -%>
|
|
7
|
+
<%% if item[:separator_before] -%>
|
|
8
|
+
<%%= tag.div "", class: "menu-sep" %>
|
|
9
|
+
<%% end -%>
|
|
10
|
+
<%% if item[:href].present? -%>
|
|
11
|
+
<%%= tag.a item[:label], href: item[:href], role: "menuitem",
|
|
12
|
+
class: class_names("danger" => item[:danger]) %>
|
|
13
|
+
<%% else -%>
|
|
14
|
+
<%%= tag.button item[:label], type: "button", role: "menuitem",
|
|
15
|
+
class: class_names("danger" => item[:danger]) %>
|
|
16
|
+
<%% end -%>
|
|
17
|
+
<%% end -%>
|
|
18
|
+
<%% end %>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<%%# locals: (value:, max:, state: nil, label: nil, **attrs) -%>
|
|
2
|
+
<%% pct = [(value.to_f / max * 100).round, 100].min -%>
|
|
3
|
+
<%%= tag.div class: "meter", **attrs do %>
|
|
4
|
+
<%% if label -%>
|
|
5
|
+
<div class="meter-label">
|
|
6
|
+
<%%= tag.span label %>
|
|
7
|
+
<%%= tag.span "#{value} / #{max}", class: "tnum" %>
|
|
8
|
+
</div>
|
|
9
|
+
<%% end -%>
|
|
10
|
+
<%%= tag.div class: "meter-track", role: "progressbar",
|
|
11
|
+
"aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max,
|
|
12
|
+
"aria-label": label do %>
|
|
13
|
+
<%%= tag.div class: class_names("meter-fill", state), style: "width: #{pct}%" %>
|
|
14
|
+
<%% end %>
|
|
15
|
+
<%% end %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%%# locals: (items:, **attrs) -%>
|
|
2
|
+
<%%= tag.nav class: "outline", "aria-label": "Outline", **attrs do %>
|
|
3
|
+
<%% items.each do |item| -%>
|
|
4
|
+
<%% level = item.fetch(:level, 1) -%>
|
|
5
|
+
<%%= tag.a item[:label], href: item.fetch(:href, "#"),
|
|
6
|
+
class: class_names("l#{level}" => level > 1),
|
|
7
|
+
"aria-current": (item[:current] ? "true" : nil) %>
|
|
8
|
+
<%% end -%>
|
|
9
|
+
<%% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%%# locals: (current:, total:, **attrs) -%>
|
|
2
|
+
<%%= tag.nav class: "pagination", "aria-label": "Pagination", **attrs do %>
|
|
3
|
+
<%%= tag.button "‹", type: "button", "aria-label": "Previous page",
|
|
4
|
+
disabled: (current <= 1) %>
|
|
5
|
+
<%% (1..total).each do |page| -%>
|
|
6
|
+
<%%= tag.button page, type: "button",
|
|
7
|
+
"aria-current": (page == current ? "page" : nil),
|
|
8
|
+
"aria-label": "Page #{page}" %>
|
|
9
|
+
<%% end -%>
|
|
10
|
+
<%%= tag.button "›", type: "button", "aria-label": "Next page",
|
|
11
|
+
disabled: (current >= total) %>
|
|
12
|
+
<%% end %>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<%%# locals: (state: :saved, text: nil, **attrs) -%>
|
|
2
|
+
<%% default = { "saved" => "All changes saved", "saving" => "Saving…", "unsaved" => "Unsaved" }[state.to_s] -%>
|
|
3
|
+
<%%= tag.span (text || default),
|
|
4
|
+
class: class_names("savestate", "saving" => state.to_s == "saving", "unsaved" => state.to_s == "unsaved"),
|
|
5
|
+
role: "status", **attrs %>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<%%# locals: (name:, options:, selected: nil, label: nil, **attrs) -%>
|
|
2
|
+
<%%= tag.div class: "seg", role: "group", "aria-label": label, **attrs do %>
|
|
3
|
+
<%% options.each do |value, option_label| -%>
|
|
4
|
+
<%%= tag.button option_label, type: "button", name: name, value: value,
|
|
5
|
+
"aria-pressed": (value.to_s == selected.to_s).to_s %>
|
|
6
|
+
<%% end -%>
|
|
7
|
+
<%% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%%# locals: (steps:, current: 1, **attrs) -%>
|
|
2
|
+
<%%= tag.ol class: "stepper", **attrs do %>
|
|
3
|
+
<%% steps.each_with_index do |label, idx| -%>
|
|
4
|
+
<%% i = idx + 1 -%>
|
|
5
|
+
<%% done = i < current -%>
|
|
6
|
+
<%%= tag.li class: class_names("done" => done, "current" => i == current),
|
|
7
|
+
"aria-current": (i == current ? "step" : nil) do %>
|
|
8
|
+
<%%= tag.span class: "dot" do %><%% if done %><%%= ui_icon(:check, size: :sm) %><span class="sr-only">Completed</span><%% else %><%%= i %><%% end %><%% end %>
|
|
9
|
+
<%%= label %>
|
|
10
|
+
<%% end %>
|
|
11
|
+
<%% end -%>
|
|
12
|
+
<%% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%%# locals: (message:, kind: :info, title: nil, **attrs) -%>
|
|
2
|
+
<%% icon = { ok: :check, alert: :warning, info: :info }.fetch(kind, :info) -%>
|
|
3
|
+
<%%= tag.div class: class_names("toast", "toast-#{kind}"), role: (kind == :alert ? "alert" : "status"), **attrs do %>
|
|
4
|
+
<%%= ui_icon(icon) %>
|
|
5
|
+
<div class="body">
|
|
6
|
+
<%% if title.present? -%>
|
|
7
|
+
<%%= tag.div title, class: "t" %>
|
|
8
|
+
<%% end -%>
|
|
9
|
+
<%%= tag.div message %>
|
|
10
|
+
</div>
|
|
11
|
+
<%%= tag.button class: "x", type: "button", "aria-label": "Dismiss" do %><%%= ui_icon(:close) %><%% end %>
|
|
12
|
+
<%% end %>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<%%# locals: (items:, label: "Formatting", **attrs) -%>
|
|
2
|
+
<%%= tag.div class: "toolbar", role: "toolbar", "aria-label": label, **attrs do %>
|
|
3
|
+
<%% items.each do |item| -%>
|
|
4
|
+
<%% if item[:sep_before] -%>
|
|
5
|
+
<%%= tag.span class: "tool-sep", "aria-hidden": "true" %>
|
|
6
|
+
<%% end -%>
|
|
7
|
+
<%%= tag.button type: "button", class: "tool", "aria-label": item[:label] do %>
|
|
8
|
+
<%% if item[:icon] -%>
|
|
9
|
+
<%%= ui_icon(item[:icon]) %>
|
|
10
|
+
<%% else -%>
|
|
11
|
+
<%%= item[:label] %>
|
|
12
|
+
<%% end -%>
|
|
13
|
+
<%% end %>
|
|
14
|
+
<%% end -%>
|
|
15
|
+
<%% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%%# locals: () -%>
|
|
2
|
+
<div class="stack">
|
|
3
|
+
<%%= ui_banner(message: "A new version is available", kind: :info) %>
|
|
4
|
+
<%%= ui_banner(message: "Something went wrong — please try again", kind: :alert) %>
|
|
5
|
+
<%%= ui_banner(message: "Scheduled maintenance Sunday 02:00 UTC", kind: :info) do %>
|
|
6
|
+
<%%= ui_button(variant: :primary, size: :sm) { "Learn more" } %>
|
|
7
|
+
<%% end %>
|
|
8
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%%# locals: () -%>
|
|
2
|
+
<div class="row-wrap">
|
|
3
|
+
<%%= ui_button(variant: :primary) { "Primary" } %>
|
|
4
|
+
<%%= ui_button { "Default" } %>
|
|
5
|
+
<%%= ui_button(variant: :ghost) { "Ghost" } %>
|
|
6
|
+
<%%= ui_button(variant: :danger) { "Danger" } %>
|
|
7
|
+
<%%= ui_button(variant: :primary, size: :sm) { "Small" } %>
|
|
8
|
+
<%%= ui_button(disabled: true) { "Disabled" } %>
|
|
9
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%%# locals: () -%>
|
|
2
|
+
<div class="stack">
|
|
3
|
+
<%%= ui_card do %>
|
|
4
|
+
<h3 class="h3">Project settings</h3>
|
|
5
|
+
<p class="small muted">Control how this project is configured.</p>
|
|
6
|
+
<%% end %>
|
|
7
|
+
|
|
8
|
+
<%%= ui_card(id: "card-export", data: { panel: "export" }) do %>
|
|
9
|
+
<h3 class="h3">Output formats</h3>
|
|
10
|
+
<p class="small muted">Files are generated on every change.</p>
|
|
11
|
+
<%% end %>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%%# locals: () -%>
|
|
2
|
+
<ul class="chapter-list">
|
|
3
|
+
<%%= ui_chapter_row(index: 1, title: "Getting started", status: :ready, status_label: "Saved") %>
|
|
4
|
+
<%%= ui_chapter_row(index: 2, title: "Core concepts", status: :progress, status_label: "Editing") %>
|
|
5
|
+
<%%= ui_chapter_row(index: 3, title: "Advanced usage", status: :neutral, status_label: "Draft") %>
|
|
6
|
+
</ul>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%%# locals: () -%>
|
|
2
|
+
<div class="stack">
|
|
3
|
+
<%%= ui_checkbox(label: "Email", name: "channels[]", value: "email", checked: true) %>
|
|
4
|
+
<%%= ui_checkbox(label: "SMS", name: "channels[]", value: "sms", checked: true) %>
|
|
5
|
+
<%%= ui_checkbox(label: "Webhook", name: "channels[]", value: "webhook", hint: "POSTs a JSON payload") %>
|
|
6
|
+
</div>
|