trek 2.0.1 → 2.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/.prettierignore +2 -0
- data/.rubocop.yml +11 -0
- data/AGENTS.md +5 -2
- data/CHANGELOG.md +47 -1
- data/Gemfile.lock +1 -1
- data/Makefile +13 -0
- data/app/assets/icons/trek/audit.svg +1 -0
- data/app/assets/icons/trek/print.svg +1 -0
- data/app/components/trek/dashboard/analytics_component/analytics_component.html.slim +2 -0
- data/app/components/trek/dashboard/analytics_component.rb +1 -0
- data/app/components/trek/dashboard/chart_component/chart_component.css +192 -0
- data/app/components/trek/dashboard/chart_component/chart_component.html.slim +6 -0
- data/app/components/trek/dashboard/chart_component/chart_component.js +1 -0
- data/app/components/trek/dashboard/chart_component.rb +27 -0
- data/app/components/trek/dashboard/tile_component/tile_component.css +40 -0
- data/app/components/trek/dashboard/tile_component/tile_component.html.slim +14 -4
- data/app/components/trek/dashboard/tile_component.rb +6 -2
- data/app/components/trek/entries/item_component/item_component.css +1 -0
- data/app/components/trek/form/actions_component/actions_component.css +6 -7
- data/app/components/trek/form/content_editor_component/content_editor_component.html.slim +10 -12
- data/app/components/trek/form/content_editor_component/content_editor_component.js +6 -0
- data/app/components/trek/form/content_editor_component.rb +4 -2
- data/app/components/trek/form/fieldset_component/fieldset_component.css +4 -3
- data/app/components/trek/form/group_component/group_component.css +5 -2
- data/app/components/trek/layout_component/layout_component.css +20 -0
- data/app/components/trek/layout_component/layout_component.html.slim +16 -0
- data/app/components/trek/layout_component/layout_component.js +35 -6
- data/app/components/trek/menu_component.rb +15 -2
- data/app/controllers/concerns/trek/model.rb +13 -6
- data/app/controllers/trek/resource_controller.rb +9 -1
- data/app/formatters/prosemirror_to_html/global_id_to_links_formatter.rb +3 -1
- data/app/helpers/trek/i18n_helper.rb +1 -1
- data/app/models/prosemirror.rb +5 -2
- data/app/views/trek/panels/images/_form.html.slim +1 -1
- data/app/views/trek/panels/images/create.turbo_stream.slim +3 -3
- data/app/views/trek/panels/images/new.html.slim +1 -1
- data/app/views/trek/panels/links/_form.html.slim +1 -1
- data/app/views/trek/panels/links/create.turbo_stream.slim +3 -3
- data/app/views/trek/panels/links/new.html.slim +1 -1
- data/app/views/trek/panels/prompts/_form.html.slim +1 -1
- data/app/views/trek/panels/prompts/create.turbo_stream.slim +3 -3
- data/app/views/trek/panels/prompts/new.html.slim +1 -1
- data/docs/.vitepress/config.mjs +2 -1
- data/docs/deploy.md +57 -0
- data/docs/getting-started.md +1 -0
- data/docs/reference/controllers.md +23 -0
- data/docs/reference/generators/install.md +2 -0
- data/docs/reference/generators/scaffold.md +12 -0
- data/lib/generators/trek/install/config_generator.rb +9 -1
- data/lib/generators/trek/install/postmark_generator.rb +2 -0
- data/lib/generators/trek/install/prosopite_generator.rb +56 -0
- data/lib/generators/trek/install/scalingo_generator.rb +19 -7
- data/lib/generators/trek/install/yarn_generator.rb +1 -1
- data/lib/generators/trek/install_generator.rb +1 -0
- data/lib/generators/trek/scaffold_generator.rb +37 -5
- data/lib/generators/trek/templates/controllers/admin/scaffold_controller.rb.tt +2 -2
- data/lib/generators/trek/templates/locales/scaffold/model.en.yml.tt +4 -3
- data/lib/generators/trek/templates/spec/requests/admin/n_plus_one_spec.rb +28 -0
- data/lib/generators/trek/templates/views/admin/scaffold/show.html.slim.tt +2 -2
- data/lib/generators/trek/upgrade/v2_generator.rb +31 -0
- data/lib/trek/version.rb +1 -1
- data/package.json +1 -1
- data/vendor/tom_select/css/tom-select.custom.css +0 -1
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 76621e65ede62feb9ed4fd8aa029352bfbdda31c814a7e9679330597a631b2f6
|
|
4
|
+
data.tar.gz: a28bce934474a0d57069466281121658c75115eb9b9b1a3056eab9c6ee03ca5f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5653df741bf84cedce799a58c4f9d8c13d8a13226793f05d1f158b4971b0b08b1806463c82fa818526c8a0454d5b489f65cae207373c581bae074e1e3ae037bd
|
|
7
|
+
data.tar.gz: aad5fb5a6e0e9010c5b611ef2ba67e7ddf112d2b983c85684ce698d32083d1da82ac24469404f3377a131d3ef1be56ec369baa81bd9cd7e71b4560a916800e62
|
data/.prettierignore
ADDED
data/.rubocop.yml
CHANGED
|
@@ -33,6 +33,17 @@ Style/StringLiteralsInInterpolation:
|
|
|
33
33
|
Layout/LineLength:
|
|
34
34
|
Max: 120
|
|
35
35
|
|
|
36
|
+
# rubocop-rails-omakase disables both so it can indent methods under
|
|
37
|
+
# `private`; we use the normal style (methods at the class-body level,
|
|
38
|
+
# `private` included) and want `rubocop --autocorrect` to fix indentation,
|
|
39
|
+
# generated files included.
|
|
40
|
+
Layout/IndentationWidth:
|
|
41
|
+
Enabled: true
|
|
42
|
+
|
|
43
|
+
Layout/IndentationConsistency:
|
|
44
|
+
Enabled: true
|
|
45
|
+
EnforcedStyle: normal
|
|
46
|
+
|
|
36
47
|
Gemspec/RequiredRubyVersion:
|
|
37
48
|
Enabled: false
|
|
38
49
|
|
data/AGENTS.md
CHANGED
|
@@ -21,11 +21,13 @@ bin/setup # bundle install + yarn install (dev setup)
|
|
|
21
21
|
bin/console # IRB with trek loaded
|
|
22
22
|
rake # default: spec + rubocop
|
|
23
23
|
rake spec # run tests (or: bundle exec rspec)
|
|
24
|
+
make lint # all linters below
|
|
25
|
+
make fix # rubocop/eslint/stylelint/prettier autocorrect
|
|
24
26
|
make release # bump version (version.rb + package.json), stamp CHANGELOG, tag, push gem + npm
|
|
25
27
|
yarn docs:dev # VitePress docs locally
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
Linters (also run automatically via lefthook on pre-commit):
|
|
30
|
+
Linters (`make lint`; also run automatically via lefthook on pre-commit):
|
|
29
31
|
|
|
30
32
|
```bash
|
|
31
33
|
bundle exec rubocop
|
|
@@ -51,5 +53,6 @@ Security: `bundle exec brakeman` · `bundle exec bundle-audit check --update`
|
|
|
51
53
|
- **Slim** linted via slim_lint; **JS/CSS** via ESLint + Stylelint + Prettier 2.8.8
|
|
52
54
|
- **i18n**: keep English + French locales in sync (`config/locales/trek.*.yml`); DeepL auto-translation via `DEEPL_AUTH_KEY`
|
|
53
55
|
- **Versioning**: version lives in both `lib/trek/version.rb` and `package.json` — keep in sync via `make release`
|
|
54
|
-
- **CHANGELOG.md**: Keep-a-Changelog format. **Every MR must add an `[Unreleased]` entry.** When the change requires action in the host app on upgrade,
|
|
56
|
+
- **CHANGELOG.md**: Keep-a-Changelog format. **Every MR must add an `[Unreleased]` entry.** When the change requires action in the host app on upgrade, end the entry with a bold `**Upgrade note:**` label followed by what to do. Use the same label when the change lets host apps remove or simplify existing patches or custom implementations (e.g. "**Upgrade note:** you can now drop your custom …") — never as an unlabelled trailing sentence.
|
|
57
|
+
- **Public code**: Trek ships publicly (gem, npm, docs), so nothing committed may mention a client or project name, nor a client's domain models or business logic — not in code, comments, specs, templates, docs, CHANGELOG or commit messages. Use neutral examples (`Page`, `Shop::Product`, an engine's `Blog::Post`). Client context belongs in GitLab issues and MR descriptions only.
|
|
55
58
|
- **CI**: GitLab CI (`.gitlab-ci.yml`), stages build → lint → security → test → deploy; docs deploy to Netlify; deps via Renovate
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.2.0] - 2026-08-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `Trek::Dashboard::ChartComponent`, a dashboard card wrapping [Chartkick](https://chartkick.com) — pass `title`, `data`, a `type` (`:line_chart`, `:bar_chart`, …), optional `info`/`options`, and a `color` (default `:slate`); it renders `public_send(type, data, **options)` inside a coloured card matching the tiles. Chartkick is an **optional** dependency: the component only includes `Chartkick::Helper` when it is loaded, so Trek keeps working without it. `Trek::Dashboard::AnalyticsComponent` gained a `renders_many :charts` slot (alongside the existing `section`) so charts can be dropped straight into the dashboard with `a.with_chart(...)`. **Upgrade note:** to use `ChartComponent`, add `gem "chartkick"` to your app and `import "chartkick/chart.js"` in your admin JS bundle (Trek does not pull Chartkick in for you).
|
|
8
|
+
- `Trek::Dashboard::TileComponent` gained a `title` option (a large heading above the text, coloured per the tile's `color`) and a `wide` option (default `false`) that spans two tile columns. Its `href` is now optional: without one the tile renders as a plain `div` instead of a link, for read-only stat tiles.
|
|
9
|
+
- The layout gains a **stacked panel**: a second panel frame (`trek--layout-stacked-panel`) that opens on top of the primary one. The content editor's link/image/prompt inserters now open there, so an editor hosted _inside_ a panel (e.g. a Sectionable payload edited in a side panel) no longer loses its unsaved content when inserting a link — the primary panel stays put underneath and the inserted element lands back in the live editor. Closing the primary panel also closes the stacked one; _Annuler_ in the stacked panel closes only that one. The stacked panel is 1rem narrower than the primary panel (only while it stacks on an open one). **Upgrade note:** host apps that override the panel views (`app/views/<engine>/panels/{links,images,prompts}/*`) must retarget them to the `trek--layout-stacked-panel` frame and the `stackedPanelCloser` target, as Trek's own views now do.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Internal links inserted through the content editor (stored as `gid://` URLs) rendered as literal `gid://…` hrefs — or were dropped by the sanitizer — in formatted content: `Prosemirror#format_content` now resolves GlobalIDs to page paths before rendering, and the editor's Link extension accepts the `gid` protocol so reopening such a link shows the linked page instead of an empty field.
|
|
14
|
+
- `Trek::Form::ActionsComponent`: the sticky footer styling (sticky positioning, `--accent-1` background, `4rem` vertical padding) now only applies to the `default` aspect. It was set on the root rule, so the `seamless` aspect — used by `Trek::DialogComponent` — was also sticky and padded inside dialogs.
|
|
15
|
+
|
|
16
|
+
## [2.1.0] - 2026-08-25
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Docs: a [Deploying to Scalingo](https://trek.etamin.studio/deploy) guide covering the trek-buildpack setup, the environment to configure, and how to migrate an app from the stock buildpacks.
|
|
21
|
+
- `trek:install:prosopite` generator (wired into `trek:install`) sets up [Prosopite](https://github.com/charkost/prosopite) N+1 query detection (#211): adds `prosopite` and `pg_query` to the `development`/`test` Gemfile group and a `config/initializers/prosopite.rb` that mounts the Rack middleware — so every request is scanned, Trek's engine controllers included — raising `Prosopite::NPlusOneQueriesError` in test and logging in development. It also generates `spec/requests/admin/n_plus_one_spec.rb`, a smoke request spec rendering the dashboard, pages index and fragments index as an admin (extend it with the app's other admin routes). Documented under [Controllers › N+1 detection](https://trek.etamin.studio/reference/controllers#n-1-detection) with the `Prosopite.pause` escape hatch. **Upgrade note:** existing apps can run `rails g trek:install:prosopite`; apps that already wired Prosopite by hand can keep their setup (the generator skips the gems when `prosopite` is already in the Gemfile and won't overwrite an existing initializer).
|
|
22
|
+
- `make lint` and `make fix` targets in Trek's own Makefile, mirroring the ones generated for host apps (RuboCop, slim-lint, ESLint, Stylelint, Prettier — the `fix` variant autocorrects all but slim-lint). A `.prettierignore` excludes the gitignored esbuild output in `app/assets/builds/`, which made the Prettier check fail on a local checkout that had run `yarn build`.
|
|
23
|
+
- `Trek::MenuComponent` items accept an `active:` option for menu entries whose pages live under other controllers: a `String` is matched as a `controller_path` prefix, a `Regexp` against the whole path, and a boolean forces (or clears) the state — e.g. `s.with_item(text: …, href: [:admin, Shop], active: "admin/shop")` keeps _Shop_ highlighted while editing its products. Without it, the item stays active when its href matches the current controller, as before.
|
|
24
|
+
- `content_editor` accepts an `autofocus:` option (default `false`): when true, the editor grabs focus on mount with the cursor at the end of the content — useful when the editor is the primary field of a side panel.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- The shared `.rubocop.yml` (Trek's own, and the one `trek:install:rubocop` copies into host apps) re-enables `Layout/IndentationWidth` and `Layout/IndentationConsistency` with `EnforcedStyle: normal`. rubocop-rails-omakase disables both to allow its house style of indenting methods under `private`; Trek's style keeps methods at the class-body level, and a plain `rubocop --autocorrect` now fixes indentation — in generated files too. **Upgrade note:** copy the two cop blocks from the template (`rails g trek:install:rubocop` will prompt to overwrite) into your app's `.rubocop.yml`, then run `bundle exec rubocop --autocorrect` once; methods indented under `private` and mis-indented generated files get fixed.
|
|
29
|
+
- `trek:install:scalingo` now targets the dedicated [trek-buildpack](https://github.com/etaminstudio/trek-buildpack) (#193) instead of the stock apt + ruby buildpack pair: it no longer generates `.buildpacks` (and removes an existing one) and prints the `env-set` commands to run (`BUILDPACK_URL` pinned to the buildpack's release tag, and the `DATABASE_URL=$SCALINGO_POSTGRESQL_URL` mapping). The buildpack pins Ruby/Node/Yarn from `.ruby-version`, the `.node-version` that `rails new` already generates, and `package.json`'s `packageManager`, runs `yarn install` once, and ships a slimmed image (~40% smaller, measured deploys down from ~3min to ~1min35) — see the buildpack README for details. **Upgrade note:** on each Scalingo app, set `BUILDPACK_URL=https://github.com/etaminstudio/trek-buildpack#v0.1.0`, delete `.buildpacks`, make sure `.node-version` exists (apps generated with `rails new -j esbuild` have it), and check that `config/database.yml` reads `DATABASE_URL` in production (mapped with `scalingo env-set 'DATABASE_URL=$SCALINGO_POSTGRESQL_URL'`).
|
|
30
|
+
- `Trek::Form::FieldsetComponent`: the fieldset title (`.c-trek--form--fieldset-title`) now shares the form group label style (`--slate-12`, semibold, `1.2rem` bottom margin) so a titled fieldset reads like a labelled group; the `section` modifier only adds its larger font size on top. Hosts that restyled the plain fieldset title themselves can drop that override.
|
|
31
|
+
- `trek:install:yarn` now installs **Yarn 4.18.0** (was 4.17.0). **Upgrade note:** existing apps can upgrade with `yarn set version 4.18.0` followed by `yarn install`.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- `trek:scaffold` now references the model top-level qualified (`::Shop::Product`) in the generated controller's `model` and in the `Admin::Dashboard::MODELS_IN_MENU` entry: once the scaffold creates the `Admin::Shop` namespace, a bare `Shop` under `module Admin` resolves to that module instead of the model — the scaffolded controller raised `NameError`, and previously generated code referencing a `Shop` model silently got the module. The generator also warns when a namespace segment matches an existing model file that `rails g model` would replace with a namespace module. (#201) **Upgrade note:** if a scaffold introduced a namespace named like an existing model, qualify that model as `::Shop` in the existing `Admin::…Controller#model` and `MODELS_IN_MENU` entries.
|
|
36
|
+
- `Trek::Entries::ItemComponent`: the details list (`.c-trek--entries--item-details`) now wraps (`flex-wrap: wrap`) instead of overflowing or squeezing its items when an entry has many details or a narrow container.
|
|
37
|
+
- `trek:install:postmark` is idempotent again: re-running it on an app where it had already run appended a second `config.action_mailer.delivery_method = :postmark` / `postmark_settings` block to `config/environments/production.rb` (the gem, credentials and mailer steps were already guarded). The config injection is now skipped when the file already sets `delivery_method = :postmark`. (#210)
|
|
38
|
+
- Namespaced models (e.g. `Shop::Product`, routed under `namespace :shop`) now work out of the box with `Trek::ResourceController` (#212): `object_params` requires `model_name.param_key` (`shop_product`) instead of the demodulized element, so create/update no longer 400; `model_element_route` no longer prefixes the namespace when the route key already carries it (`shop_product`), so the delete button and list item links build `admin_shop_product_path` instead of the double-namespaced `admin_shop_shop_product_path` — models from isolated engines with relative naming (e.g. an engine's `Blog::Post`, route key `post`) keep the `[:blog, :post]` form; the controller defines `implicit_authorization_target` (`@object || model`) so `authorized(params)` and `Trek::Reorderable#reorder`'s bare `authorize!` find the policy; and `i18n_model_gender` reads `activerecord.models.<i18n_key>.gender` (`shop/product`), so `i18n_resource_label` picks the declared gender instead of the neutral fallback. **Upgrade note:** hosts that worked around this with a controller concern overriding `implicit_authorization_target`/`object_params`/`model_element_route`, or a prepend on `Trek::I18nHelper#i18n_model_gender`, can drop them.
|
|
39
|
+
- `Trek::Form::GroupComponent` now styles `input[type="time"]` like `input[type="date"]` (field frame, 16rem max width, lighter picker colour); `time_field`s used to render as bare browser inputs.
|
|
40
|
+
- Removed the dangling `sourceMappingURL` comment from the vendored `tom_select/css/tom-select.custom.css`: the referenced `.map` file is not shipped, so every host-app esbuild build (including `assets:precompile` on deploy) logged a `missing-source-map` debug warning. (#209)
|
|
41
|
+
- `trek:scaffold` now re-indents the generated controller and policy: for a namespaced model the nested `module` wrappers were emitted without indenting the class body, tripping the host app's linters. The generator runs a targeted `rubocop --autocorrect --only Layout/IndentationWidth,Layout/IndentationConsistency` on both files (forced with `--only` since rubocop-rails-omakase leaves those cops disabled). (#199)
|
|
42
|
+
- The `config/initializers/host.rb` generated by `trek:install:config` no longer references `ActionMailer::Base` during the initializer phase: doing so fires Action Mailer's load hook before the app's autoloader can resolve app classes, so an app configuring a custom `config.action_mailer.delivery_job` (e.g. `"MailDeliveryJob"`) crashed with a `NameError` on every production boot — breaking `assets:precompile` on deploy. The assignment is now deferred with `ActiveSupport.on_load(:action_mailer)`. (#208) **Upgrade note:** apply the same change to your app's `config/initializers/host.rb` — replace the `ActionMailer::Base.default_url_options = … if Rails.env.production?` line with the deferred block (see the generator template).
|
|
43
|
+
- `trek:scaffold` now generates the right controller class for a namespaced model (e.g. `rails g trek:scaffold Shop::Product`): the template used the still-namespaced `controller_class_name` inside the per-namespace `module` wrappers, defining `Admin::Shop::Shop::ProductsController` while the generated route expected `Admin::Shop::ProductsController` — every request 404ed with an uninitialized constant. The class name is now demodulized, like the policy template already did. (#200)
|
|
44
|
+
- The `show.html.slim` generated by `trek:scaffold` no longer ends with a trailing blank line: the attribute loop emitted its separator after every item instead of between items, so slim-lint failed in host apps with `TrailingBlankLines: Multiple empty lines in the end of file`. (#205)
|
|
45
|
+
- `trek:upgrade:v2` now upgrades Yarn to 4.18.0 first (applying the 0.1.26 upgrade note: `yarn set version` + dropping the `interactive-tools` plugin) when the app still runs Yarn 3. Previously the generator wrote `npmPreapprovedPackages` — a Yarn 4-only setting — unconditionally, which under Yarn 3 is a hard error that breaks every yarn command, including the generator's own `yarn add` step.
|
|
46
|
+
- Clarified the 0.1.26 changelog upgrade note about the Yarn minimal-age-gate exemption: it only applied to the pre-2.0.0 private GitLab distribution — since 2.0.0 the (Yarn 4-only) `npmPreapprovedPackages` entry, set up automatically by `trek:install` and `trek:upgrade:v2`, replaces it.
|
|
47
|
+
- Fixed whitespace in `model.en.yml.tt` which caused prettier to fail.
|
|
48
|
+
|
|
3
49
|
## [2.0.1] - 2026-08-18
|
|
4
50
|
|
|
5
51
|
### Fixed
|
|
@@ -41,7 +87,7 @@
|
|
|
41
87
|
### Fixed
|
|
42
88
|
|
|
43
89
|
- Added the missing French translations that fell back to English: `admin.dialog_component.{title,text}` (the confirmation dialog shown on every destroy action) and `admin.{pages,fragments}.{show.intro,reorder.success,search.alert}`. Also added the English `admin.section`/`admin.sections` flash messages that previously existed only in French. (#194)
|
|
44
|
-
- `trek:install:yarn` now exempts the `@etaminstudio` scope from Yarn's minimal-age gate (`npmMinimalAgeGate: 0` under `npmScopes.etaminstudio` in the generated `.yarnrc.yml`), so a freshly published `@etaminstudio/trek` release installs without being quarantined; the gate still applies to public packages. **Upgrade note:** existing apps can add `npmMinimalAgeGate: 0` under `npmScopes.etaminstudio` in their `.yarnrc.yml`.
|
|
90
|
+
- `trek:install:yarn` now exempts the `@etaminstudio` scope from Yarn's minimal-age gate (`npmMinimalAgeGate: 0` under `npmScopes.etaminstudio` in the generated `.yarnrc.yml`), so a freshly published `@etaminstudio/trek` release installs without being quarantined; the gate still applies to public packages. **Upgrade note:** existing apps can add `npmMinimalAgeGate: 0` under `npmScopes.etaminstudio` in their `.yarnrc.yml`. _(Only relevant to the private GitLab distribution used before 2.0.0: the `@etaminstudio` scope is retired since then, so this scope exemption does nothing for the public unscoped `trek` package. From 2.0.0 the equivalent is `trek` listed under `npmPreapprovedPackages` — a Yarn 4-only setting, hence the Yarn upgrade above — which `trek:install` generates and `trek:upgrade:v2` adds during migration, so no manual step is needed.)_
|
|
45
91
|
- `trek:install:locales` no longer generates an invalid `config/initializers/locales.rb` (`I18n.available_locales = [:]` / `I18n.default_locale = :`) when passed no or blank locales. A blank option/env now falls back to the default (`en, fr`), blank entries are dropped, and the list can never end up empty. (#169)
|
|
46
92
|
|
|
47
93
|
## [0.1.25] - 2026-07-01
|
data/Gemfile.lock
CHANGED
data/Makefile
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
.PHONY: *
|
|
2
2
|
|
|
3
|
+
lint:
|
|
4
|
+
bundle exec rubocop
|
|
5
|
+
bundle exec slim-lint "app/**/*.slim"
|
|
6
|
+
yarn eslint "app/**/*.js"
|
|
7
|
+
yarn stylelint "app/**/*.css"
|
|
8
|
+
yarn prettier --check "{app,config,lib,spec}/**/*.{css,js,json,md,yml}"
|
|
9
|
+
|
|
10
|
+
fix:
|
|
11
|
+
bundle exec rubocop --autocorrect
|
|
12
|
+
yarn eslint "app/**/*.js" --fix
|
|
13
|
+
yarn stylelint "app/**/*.css" --fix
|
|
14
|
+
yarn prettier --write "{app,config,lib,spec}/**/*.{css,js,json,md,yml}"
|
|
15
|
+
|
|
3
16
|
release:
|
|
4
17
|
@read -p "New version (current: $$(sed -n 's/.*VERSION = "\(.*\)"/\1/p' lib/trek/version.rb)): " version; \
|
|
5
18
|
if [ -z "$$version" ]; then echo "Version cannot be empty"; exit 1; fi; \
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<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="M14 18a2 2 0 0 0-4 0"/><path d="m19 11-2.11-6.657a2 2 0 0 0-2.752-1.148l-1.276.61A2 2 0 0 1 12 4H8.5a2 2 0 0 0-1.925 1.456L5 11"/><path d="M2 11h20"/><circle cx="17" cy="18" r="3"/><circle cx="7" cy="18" r="3"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<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="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><path d="M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6"/><rect x="6" y="14" width="12" height="8" rx="1"/></svg>
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
border: 0.1rem solid;
|
|
3
|
+
border-radius: 0.8rem;
|
|
4
|
+
font-weight: var(--semibold);
|
|
5
|
+
margin-bottom: 2rem;
|
|
6
|
+
max-width: 83.7rem;
|
|
7
|
+
padding: 2rem;
|
|
8
|
+
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out,
|
|
9
|
+
color 0.2s ease-in-out;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.root:last-child {
|
|
13
|
+
margin-bottom: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.title {
|
|
17
|
+
color: var(--mint-12);
|
|
18
|
+
font-size: 1.6rem;
|
|
19
|
+
font-weight: var(--semibold);
|
|
20
|
+
margin: 0 0 0.5rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.data {
|
|
24
|
+
margin-bottom: 2rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.info {
|
|
28
|
+
color: var(--slate-11);
|
|
29
|
+
font-size: 1.4rem;
|
|
30
|
+
font-weight: var(--medium);
|
|
31
|
+
padding-bottom: 1.6rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.root.color-amber {
|
|
35
|
+
--icon-color: var(--amber-11);
|
|
36
|
+
|
|
37
|
+
background-color: var(--amber-2);
|
|
38
|
+
border-color: var(--amber-5);
|
|
39
|
+
color: var(--amber-12);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.root.color-amber:hover {
|
|
43
|
+
--icon-color: var(--amber-12);
|
|
44
|
+
|
|
45
|
+
background-color: var(--amber-3);
|
|
46
|
+
border-color: var(--amber-6);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.root.color-amber:active {
|
|
50
|
+
background-color: var(--amber-5);
|
|
51
|
+
border-color: var(--amber-7);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.root.color-lime {
|
|
55
|
+
--icon-color: var(--lime-11);
|
|
56
|
+
|
|
57
|
+
background-color: var(--lime-2);
|
|
58
|
+
border-color: var(--lime-5);
|
|
59
|
+
color: var(--lime-12);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.root.color-lime:hover {
|
|
63
|
+
--icon-color: var(--lime-12);
|
|
64
|
+
|
|
65
|
+
background-color: var(--lime-3);
|
|
66
|
+
border-color: var(--lime-6);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.root.color-lime:active {
|
|
70
|
+
background-color: var(--lime-5);
|
|
71
|
+
border-color: var(--lime-7);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.root.color-mint {
|
|
75
|
+
--icon-color: var(--mint-11);
|
|
76
|
+
|
|
77
|
+
background-color: var(--mint-2);
|
|
78
|
+
border-color: var(--mint-5);
|
|
79
|
+
color: var(--mint-12);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.root.color-mint:hover {
|
|
83
|
+
--icon-color: var(--mint-12);
|
|
84
|
+
|
|
85
|
+
background-color: var(--mint-3);
|
|
86
|
+
border-color: var(--mint-6);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.root.color-mint:active {
|
|
90
|
+
background-color: var(--mint-5);
|
|
91
|
+
border-color: var(--mint-7);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.root.color-purple {
|
|
95
|
+
--icon-color: var(--purple-11);
|
|
96
|
+
|
|
97
|
+
background-color: var(--purple-2);
|
|
98
|
+
border-color: var(--purple-5);
|
|
99
|
+
color: var(--purple-12);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.root.color-purple:hover {
|
|
103
|
+
--icon-color: var(--purple-12);
|
|
104
|
+
|
|
105
|
+
background-color: var(--purple-3);
|
|
106
|
+
border-color: var(--purple-6);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.root.color-purple:active {
|
|
110
|
+
background-color: var(--purple-5);
|
|
111
|
+
border-color: var(--purple-7);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.root.color-sky {
|
|
115
|
+
--icon-color: var(--sky-11);
|
|
116
|
+
|
|
117
|
+
background-color: var(--sky-2);
|
|
118
|
+
border-color: var(--sky-5);
|
|
119
|
+
color: var(--sky-12);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.root.color-sky:hover {
|
|
123
|
+
--icon-color: var(--sky-12);
|
|
124
|
+
|
|
125
|
+
background-color: var(--sky-3);
|
|
126
|
+
border-color: var(--sky-6);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.root.color-sky:active {
|
|
130
|
+
background-color: var(--sky-5);
|
|
131
|
+
border-color: var(--sky-7);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.root.color-slate {
|
|
135
|
+
--icon-color: var(--slate-11);
|
|
136
|
+
|
|
137
|
+
background-color: var(--slate-2);
|
|
138
|
+
border-color: var(--slate-5);
|
|
139
|
+
color: var(--slate-12);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.root.color-slate:hover {
|
|
143
|
+
--icon-color: var(--slate-12);
|
|
144
|
+
|
|
145
|
+
background-color: var(--slate-3);
|
|
146
|
+
border-color: var(--slate-6);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.root.color-slate:active {
|
|
150
|
+
background-color: var(--slate-5);
|
|
151
|
+
border-color: var(--slate-7);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.root.color-teal {
|
|
155
|
+
--icon-color: var(--teal-11);
|
|
156
|
+
|
|
157
|
+
background-color: var(--teal-2);
|
|
158
|
+
border-color: var(--teal-5);
|
|
159
|
+
color: var(--teal-12);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.root.color-teal:hover {
|
|
163
|
+
--icon-color: var(--teal-12);
|
|
164
|
+
|
|
165
|
+
background-color: var(--teal-3);
|
|
166
|
+
border-color: var(--teal-6);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.root.color-teal:active {
|
|
170
|
+
background-color: var(--teal-5);
|
|
171
|
+
border-color: var(--teal-7);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.root.color-violet {
|
|
175
|
+
--icon-color: var(--violet-11);
|
|
176
|
+
|
|
177
|
+
background-color: var(--violet-2);
|
|
178
|
+
border-color: var(--violet-5);
|
|
179
|
+
color: var(--violet-12);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.root.color-violet:hover {
|
|
183
|
+
--icon-color: var(--violet-12);
|
|
184
|
+
|
|
185
|
+
background-color: var(--violet-3);
|
|
186
|
+
border-color: var(--violet-6);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.root.color-violet:active {
|
|
190
|
+
background-color: var(--violet-5);
|
|
191
|
+
border-color: var(--violet-7);
|
|
192
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./chart_component.css";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Trek
|
|
4
|
+
class Dashboard::ChartComponent < Trek::Component
|
|
5
|
+
# Chartkick is an optional dependency: host apps that use this component must
|
|
6
|
+
# add `gem "chartkick"` and import "chartkick/chart.js" in their JS bundle.
|
|
7
|
+
include Chartkick::Helper if defined?(Chartkick)
|
|
8
|
+
|
|
9
|
+
option :title
|
|
10
|
+
option :info, optional: true
|
|
11
|
+
option :data
|
|
12
|
+
option :type
|
|
13
|
+
option :options, optional: true, default: proc { {} }
|
|
14
|
+
option :color, default: proc { :slate }
|
|
15
|
+
|
|
16
|
+
def chart
|
|
17
|
+
public_send(type, data, **options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def classes
|
|
21
|
+
class_names(
|
|
22
|
+
root_class,
|
|
23
|
+
class_for("color-#{color}")
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
width: 20rem;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
.root.wide {
|
|
24
|
+
width: calc(40rem + 1.2rem);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.title {
|
|
28
|
+
font-size: 3.6rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
.root:hover .icon {
|
|
24
32
|
transform: scale(1.125);
|
|
25
33
|
}
|
|
@@ -58,6 +66,10 @@
|
|
|
58
66
|
border-color: var(--amber-7);
|
|
59
67
|
}
|
|
60
68
|
|
|
69
|
+
.root.color-amber .title {
|
|
70
|
+
color: var(--amber-11);
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
.root.color-lime {
|
|
62
74
|
--icon-color: var(--lime-11);
|
|
63
75
|
|
|
@@ -78,6 +90,10 @@
|
|
|
78
90
|
border-color: var(--lime-7);
|
|
79
91
|
}
|
|
80
92
|
|
|
93
|
+
.root.color-lime .title {
|
|
94
|
+
color: var(--lime-11);
|
|
95
|
+
}
|
|
96
|
+
|
|
81
97
|
.root.color-mint {
|
|
82
98
|
--icon-color: var(--mint-11);
|
|
83
99
|
|
|
@@ -98,6 +114,10 @@
|
|
|
98
114
|
border-color: var(--mint-7);
|
|
99
115
|
}
|
|
100
116
|
|
|
117
|
+
.root.color-mint .title {
|
|
118
|
+
color: var(--mint-11);
|
|
119
|
+
}
|
|
120
|
+
|
|
101
121
|
.root.color-purple {
|
|
102
122
|
--icon-color: var(--purple-11);
|
|
103
123
|
|
|
@@ -118,6 +138,10 @@
|
|
|
118
138
|
border-color: var(--purple-7);
|
|
119
139
|
}
|
|
120
140
|
|
|
141
|
+
.root.color-purple .title {
|
|
142
|
+
color: var(--purple-11);
|
|
143
|
+
}
|
|
144
|
+
|
|
121
145
|
.root.color-sky {
|
|
122
146
|
--icon-color: var(--sky-11);
|
|
123
147
|
|
|
@@ -138,6 +162,10 @@
|
|
|
138
162
|
border-color: var(--sky-7);
|
|
139
163
|
}
|
|
140
164
|
|
|
165
|
+
.root.color-sky .title {
|
|
166
|
+
color: var(--sky-11);
|
|
167
|
+
}
|
|
168
|
+
|
|
141
169
|
.root.color-slate {
|
|
142
170
|
--icon-color: var(--slate-11);
|
|
143
171
|
|
|
@@ -158,6 +186,10 @@
|
|
|
158
186
|
border-color: var(--slate-7);
|
|
159
187
|
}
|
|
160
188
|
|
|
189
|
+
.root.color-slate .title {
|
|
190
|
+
color: var(--slate-11);
|
|
191
|
+
}
|
|
192
|
+
|
|
161
193
|
.root.color-teal {
|
|
162
194
|
--icon-color: var(--teal-11);
|
|
163
195
|
|
|
@@ -178,6 +210,10 @@
|
|
|
178
210
|
border-color: var(--teal-7);
|
|
179
211
|
}
|
|
180
212
|
|
|
213
|
+
.root.color-teal .title {
|
|
214
|
+
color: var(--teal-11);
|
|
215
|
+
}
|
|
216
|
+
|
|
181
217
|
.root.color-violet {
|
|
182
218
|
--icon-color: var(--violet-11);
|
|
183
219
|
|
|
@@ -197,3 +233,7 @@
|
|
|
197
233
|
background-color: var(--violet-5);
|
|
198
234
|
border-color: var(--violet-7);
|
|
199
235
|
}
|
|
236
|
+
|
|
237
|
+
.root.color-violet .title {
|
|
238
|
+
color: var(--violet-11);
|
|
239
|
+
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
- if href.present?
|
|
2
|
+
= link_to href, class: classes, rel: rel, target: target
|
|
3
|
+
- if title
|
|
4
|
+
span(class=class_for("title")) = title
|
|
5
|
+
- if icon
|
|
6
|
+
= render Trek::IconComponent.new(icon, classnames: class_for("icon"))
|
|
7
|
+
span(class=class_for("text")) = formatted_text
|
|
8
|
+
- else
|
|
9
|
+
div(class=classes)
|
|
10
|
+
- if title
|
|
11
|
+
span(class=class_for("title")) = title
|
|
12
|
+
- if icon
|
|
13
|
+
= render Trek::IconComponent.new(icon, classnames: class_for("icon"))
|
|
14
|
+
span(class=class_for("text")) = formatted_text
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
module Trek
|
|
4
4
|
class Dashboard::TileComponent < Trek::Component
|
|
5
5
|
option :color, default: proc { :slate }
|
|
6
|
-
option :href
|
|
6
|
+
option :href, optional: true
|
|
7
7
|
option :icon, optional: true
|
|
8
8
|
option :rel, optional: true
|
|
9
9
|
option :target, optional: true
|
|
10
10
|
option :text
|
|
11
|
+
option :title, optional: true
|
|
11
12
|
option :allowed_to, default: -> { true }
|
|
13
|
+
# Spans two tile columns (including the gap between them).
|
|
14
|
+
option :wide, default: proc { false }
|
|
12
15
|
|
|
13
16
|
def render?
|
|
14
17
|
allowed_to
|
|
@@ -17,7 +20,8 @@ module Trek
|
|
|
17
20
|
def classes
|
|
18
21
|
class_names(
|
|
19
22
|
root_class,
|
|
20
|
-
class_for("color-#{color}")
|
|
23
|
+
class_for("color-#{color}"),
|
|
24
|
+
class_for("wide") => wide
|
|
21
25
|
)
|
|
22
26
|
end
|
|
23
27
|
|
|
@@ -2,14 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
.root {
|
|
4
4
|
align-items: center;
|
|
5
|
-
background-color: var(--accent-1);
|
|
6
|
-
bottom: 0;
|
|
7
5
|
display: flex;
|
|
8
6
|
max-width: 120rem;
|
|
9
|
-
padding: 4rem 0;
|
|
10
|
-
position: sticky;
|
|
11
|
-
width: 100%;
|
|
12
|
-
z-index: 2;
|
|
13
7
|
}
|
|
14
8
|
|
|
15
9
|
.buttons {
|
|
@@ -36,9 +30,14 @@
|
|
|
36
30
|
|
|
37
31
|
/* aspect */
|
|
38
32
|
.root.aspect-default {
|
|
33
|
+
background-color: var(--accent-1);
|
|
39
34
|
border-top: 1px solid var(--slate-6);
|
|
35
|
+
bottom: 0;
|
|
40
36
|
margin-top: 3.2rem;
|
|
41
|
-
padding
|
|
37
|
+
padding: 3.2rem 0 4rem;
|
|
38
|
+
position: sticky;
|
|
39
|
+
width: 100%;
|
|
40
|
+
z-index: 2;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
/* .root.aspect-seamless {
|
|
@@ -29,16 +29,14 @@ div(
|
|
|
29
29
|
/ data-action=stimulus_action("toggleCode")
|
|
30
30
|
/ ) code
|
|
31
31
|
|
|
32
|
-
/
|
|
32
|
+
/ The panel opening is handled by the hidden button's
|
|
33
|
+
openStackedPanelWhenLoaded action — a direct openPanel here would
|
|
34
|
+
pop the (possibly empty) primary panel alongside the stacked one.
|
|
33
35
|
button.link(
|
|
34
36
|
class=class_for("button")
|
|
35
37
|
type="button"
|
|
36
|
-
data-action=
|
|
37
|
-
stimulus_action("toggleLink"),
|
|
38
|
-
"trek--layout#openPanel",
|
|
39
|
-
].join(" ")
|
|
38
|
+
data-action=stimulus_action("toggleLink")
|
|
40
39
|
) = render Trek::IconComponent.new("trek/link", title: t(".link"))
|
|
41
|
-
/ slim-lint:enable ControlStatementSpacing
|
|
42
40
|
|
|
43
41
|
= link_to "",
|
|
44
42
|
new_link_path,
|
|
@@ -46,8 +44,8 @@ div(
|
|
|
46
44
|
hidden: true,
|
|
47
45
|
data: stimulus_target_hash("hiddenLinkButton").merge( \
|
|
48
46
|
'base-path': new_link_path,
|
|
49
|
-
action: "trek--layout#
|
|
50
|
-
'turbo-frame': "trek--layout-panel",
|
|
47
|
+
action: "trek--layout#openStackedPanelWhenLoaded",
|
|
48
|
+
'turbo-frame': "trek--layout-stacked-panel",
|
|
51
49
|
)
|
|
52
50
|
|
|
53
51
|
button(
|
|
@@ -239,8 +237,8 @@ div(
|
|
|
239
237
|
hidden: true,
|
|
240
238
|
data: stimulus_target_hash("hiddenPromptsButton").merge( \
|
|
241
239
|
'base-path': new_prompt_path,
|
|
242
|
-
action: "trek--layout#
|
|
243
|
-
'turbo-frame': "trek--layout-panel",
|
|
240
|
+
action: "trek--layout#openStackedPanelWhenLoaded",
|
|
241
|
+
'turbo-frame': "trek--layout-stacked-panel",
|
|
244
242
|
)
|
|
245
243
|
= link_to "",
|
|
246
244
|
new_image_path,
|
|
@@ -248,6 +246,6 @@ div(
|
|
|
248
246
|
hidden: true,
|
|
249
247
|
data: stimulus_target_hash("hiddenImageButton").merge( \
|
|
250
248
|
'base-path': new_image_path,
|
|
251
|
-
action: "trek--layout#
|
|
252
|
-
'turbo-frame': "trek--layout-panel",
|
|
249
|
+
action: "trek--layout#openStackedPanelWhenLoaded",
|
|
250
|
+
'turbo-frame': "trek--layout-stacked-panel",
|
|
253
251
|
)
|