daisyui 2.0.0 → 2.0.2
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 +43 -0
- data/README.md +42 -1
- data/app/javascript/daisy_ui/controllers/daisy_dropdown_controller.min.js +4 -0
- data/app/javascript/daisy_ui/controllers/daisy_dropdown_controller.min.js.map +10 -0
- data/app/javascript/daisy_ui/controllers/daisy_tooltip_controller.min.js +4 -0
- data/app/javascript/daisy_ui/controllers/daisy_tooltip_controller.min.js.map +10 -0
- data/config/importmap.rb +12 -3
- data/lib/daisy_ui/base.rb +44 -2
- data/lib/daisy_ui/button.rb +12 -0
- data/lib/daisy_ui/chat.rb +1 -0
- data/lib/daisy_ui/checkbox.rb +1 -0
- data/lib/daisy_ui/collapsible_sub_menu.rb +1 -0
- data/lib/daisy_ui/configurable.rb +59 -0
- data/lib/daisy_ui/dropdown.rb +1 -0
- data/lib/daisy_ui/engine.rb +19 -9
- data/lib/daisy_ui/indicator.rb +1 -0
- data/lib/daisy_ui/modal.rb +1 -0
- data/lib/daisy_ui/steps.rb +1 -0
- data/lib/daisy_ui/sub_menu.rb +2 -0
- data/lib/daisy_ui/tab.rb +6 -0
- data/lib/daisy_ui/tab_with_content.rb +1 -0
- data/lib/daisy_ui/tab_without_content.rb +1 -0
- data/lib/daisy_ui/tooltip.rb +1 -0
- data/lib/daisy_ui/unknown_modifier_error.rb +31 -0
- data/lib/daisy_ui/updated_at.rb +1 -1
- data/lib/daisy_ui/version.rb +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 784f97cabf197c081daba3827d5d7cc67a4bfe68b1be95fd038cecaae9da127c
|
|
4
|
+
data.tar.gz: e1cc4be0135efdf03ed592a8c0d73e2b12d81c4cee47dacd7008ac34aa8bdbbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d6331604dbf5bdd7b419dfcbb5494cf7569a1709b3d9b17dd7fe08d56d1e3fed14b16c4d1925534a4efdce23e4e85347901b5687580f466e02aa9081f5ca646f
|
|
7
|
+
data.tar.gz: 002c0ad206f54764209eb6ae752c852a91369adb97c84910fe39daf19ca993842b773f6f60ad27da12cc88d25e6d7df8cc6415018408a541c99533d214432193
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
### Unknown modifiers are reported (#54)
|
|
4
|
+
|
|
5
|
+
A positional or responsive modifier a component does not know used to render
|
|
6
|
+
nothing, silently. `config.unknown_modifiers` now decides what happens:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
DaisyUI.configure do |config|
|
|
10
|
+
config.unknown_modifiers = :raise # :raise | :warn | :ignore | ->(error) { ... }
|
|
11
|
+
end
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- The default is `:raise` under Rails in development and test, and `:warn`
|
|
15
|
+
everywhere else (production, plain Ruby). A Rails app's test suite can go red
|
|
16
|
+
after upgrading if it passes a modifier a component never had, e.g.
|
|
17
|
+
`Toggle(:checked)` or `Radio(:checked)` (use `checked: true`): fix the call,
|
|
18
|
+
register the modifier, or set `:warn`/`:ignore`.
|
|
19
|
+
- `:raise` raises `DaisyUI::UnknownModifierError` (an `ArgumentError`) naming
|
|
20
|
+
the component, the modifier and the closest registered ones.
|
|
21
|
+
- `:warn` and a callable fire once per component (or part, so `Chat` and
|
|
22
|
+
`Chat#bubble` count separately) and modifier. Thread-safe; the memory of what
|
|
23
|
+
was reported resets after 1,000 distinct keys, so a modifier may be reported
|
|
24
|
+
again after that. Use a callable to send it to an
|
|
25
|
+
error tracker:
|
|
26
|
+
`config.unknown_modifiers = ->(error) { Appsignal.report_error(error) }`.
|
|
27
|
+
- Known means the component's `register_modifiers`, `config.modifiers.add`, and
|
|
28
|
+
the new `register_flags` for positional symbols a component handles itself
|
|
29
|
+
without a class (Dropdown's `:popover`, Modal's `:tap_outside_to_close`, ...).
|
|
30
|
+
Flags are not valid in `responsive:`.
|
|
31
|
+
- `Chat#bubble`, `Steps#step` and `Indicator#item` check their modifiers too
|
|
32
|
+
(`DaisyUI::Chat#bubble has no modifier :primray`).
|
|
33
|
+
- Keyword booleans are not validated: unknown keywords stay HTML attributes.
|
|
34
|
+
|
|
3
35
|
### Upgrading to 2.0
|
|
4
36
|
|
|
5
37
|
Components now merge their classes: when two classes conflict, the later one
|
|
@@ -67,6 +99,12 @@ wins. `Button(:sm, class: "btn-lg")` renders `btn btn-lg` (1.x rendered
|
|
|
67
99
|
|
|
68
100
|
### Changed
|
|
69
101
|
|
|
102
|
+
- The gem ships minified Stimulus controllers with linked sourcemaps, and the
|
|
103
|
+
importmap pins them without preloading, so with the lazy registration the
|
|
104
|
+
README shows (`lazyLoadControllersFrom`) a page that never uses one never
|
|
105
|
+
fetches it. `daisy_dropdown_controller` 6.1 KB → 2.8 KB (2.4 → 1.3 KB gzipped),
|
|
106
|
+
`daisy_tooltip_controller` 8.8 KB → 5.2 KB (2.4 → 1.8 KB gzipped). The
|
|
107
|
+
`daisy_ui/controllers/*` specifiers and the bundler import paths are unchanged.
|
|
70
108
|
- Maintainers cut releases with `bin/release` (`list`, `--dry-run`, `patch`/`minor`/`major`/`X.Y.Z`), which checks the lockfiles for gems that do not accept the new version yet and then drives `rake release`.
|
|
71
109
|
|
|
72
110
|
- Range component: added range-vertical modifier for vertical orientation (DaisyUI 5.6)
|
|
@@ -74,7 +112,12 @@ wins. `Button(:sm, class: "btn-lg")` renders `btn btn-lg` (1.x rendered
|
|
|
74
112
|
|
|
75
113
|
### Fixed
|
|
76
114
|
|
|
115
|
+
- Modifiers added with `config.modifiers.add(component: X)` now apply to
|
|
116
|
+
subclasses of `X` too; the closest class wins.
|
|
77
117
|
- Popover tooltips no longer clip arrows that extend beyond the tooltip card.
|
|
118
|
+
- Button renders the `disabled` attribute on `button`/`input` elements when given
|
|
119
|
+
`:disabled` or `disabled: true`, so a disabled submit button no longer submits
|
|
120
|
+
its form from the keyboard (#52).
|
|
78
121
|
|
|
79
122
|
## [0.1.0] - 2024-08-02
|
|
80
123
|
|
data/README.md
CHANGED
|
@@ -177,6 +177,38 @@ arrays, `nil` and `false`; returns a frozen String). Opt out globally with
|
|
|
177
177
|
`config.class_merge.enabled = false`, or per component by overriding the
|
|
178
178
|
private `merge_classes`.
|
|
179
179
|
|
|
180
|
+
## Unknown modifiers
|
|
181
|
+
|
|
182
|
+
A positional or responsive modifier the component does not know
|
|
183
|
+
(`Toggle(:primray)`) is reported instead of silently rendering nothing:
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
DaisyUI.configure do |config|
|
|
187
|
+
# Default: :raise under Rails in development/test, :warn everywhere else.
|
|
188
|
+
config.unknown_modifiers = :raise # raises DaisyUI::UnknownModifierError
|
|
189
|
+
# Or :warn (Kernel#warn), :ignore, or a callable such as
|
|
190
|
+
# ->(error) { Appsignal.report_error(error) }. :warn and a callable fire once
|
|
191
|
+
# per component (or sub-component part, e.g. Chat#bubble) and modifier; the
|
|
192
|
+
# memory of what was reported resets after 1,000 distinct keys.
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Known modifiers are the component's `register_modifiers`, `config.modifiers.add`
|
|
197
|
+
(for the component or any DaisyUI ancestor), and `register_flags`: positional
|
|
198
|
+
symbols your component handles itself without emitting a class. Flags are not
|
|
199
|
+
valid in `responsive:`, since they have no class to prefix. Sub-component
|
|
200
|
+
methods with their own modifiers (`Chat#bubble`, `Steps#step`,
|
|
201
|
+
`Indicator#item`) are checked too.
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
class Widget < DaisyUI::Base
|
|
205
|
+
register_flags :collapsed # Widget(:collapsed) is valid; read it via `modifiers.include?(:collapsed)`
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Keyword arguments are not validated: `Toggle(primray: true)` renders a
|
|
210
|
+
`primray` attribute, because unknown keywords are HTML attributes.
|
|
211
|
+
|
|
180
212
|
# Compatibility Notes
|
|
181
213
|
|
|
182
214
|
## @tailwindcss/forms plugin
|
|
@@ -270,6 +302,10 @@ import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
|
|
|
270
302
|
lazyLoadControllersFrom("daisy_ui/controllers", application)
|
|
271
303
|
```
|
|
272
304
|
|
|
305
|
+
The pin resolves to a minified build with a linked sourcemap (devtools shows the
|
|
306
|
+
readable source) and is not preloaded: a page fetches the controller the first
|
|
307
|
+
time one of its elements appears.
|
|
308
|
+
|
|
273
309
|
Then opt in per call site:
|
|
274
310
|
|
|
275
311
|
```ruby
|
|
@@ -343,7 +379,7 @@ end
|
|
|
343
379
|
Register the gem controllers once with
|
|
344
380
|
`lazyLoadControllersFrom("daisy_ui/controllers", application)`, as shown in the
|
|
345
381
|
dropdown controller section above. Importmap-rails applications receive the
|
|
346
|
-
controller pin from the gem automatically. Bundler applications can import
|
|
382
|
+
controller pin (minified, not preloaded) from the gem automatically. Bundler applications can import
|
|
347
383
|
`app/javascript/daisy_ui/controllers/daisy_tooltip_controller.js` and register
|
|
348
384
|
it as `daisy-tooltip`.
|
|
349
385
|
|
|
@@ -416,6 +452,11 @@ Which produces:
|
|
|
416
452
|
|
|
417
453
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
418
454
|
|
|
455
|
+
After editing a Stimulus controller in `app/javascript/daisy_ui/controllers/`,
|
|
456
|
+
rebuild its minified twin with `bundle exec rake build:js` (needs the bun in
|
|
457
|
+
`.bun-version`) and commit the `.min.js` and `.min.js.map`. CI runs
|
|
458
|
+
`bundle exec rake build:js_check` and fails when they are stale.
|
|
459
|
+
|
|
419
460
|
# Contributing
|
|
420
461
|
|
|
421
462
|
Bug reports and pull requests are welcome on GitHub at https://github.com/mhenrixon/daisyui. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/mhenrixon/daisyui/blob/main/CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{Controller as l}from"@hotwired/stimulus";var h=4,d='[role="menuitem"]:not([aria-disabled="true"]), a[href]:not([aria-disabled="true"]), button:not([disabled])';class p extends l{static targets=["trigger","menu"];static values={keyboard:{type:Boolean,default:!1}};connect(){if(this.menuEl=this.hasMenuTarget?this.menuTarget:this.element.querySelector("[popover]"),this.triggerEl=this.hasTriggerTarget?this.triggerTarget:this.element.querySelector("[popovertarget]"),!this.menuEl)return;this.needsJsPositioning=!this.#d(),this.onToggle=(t)=>this.#n(t),this.menuEl.addEventListener("toggle",this.onToggle)}disconnect(){this.menuEl?.removeEventListener("toggle",this.onToggle),this.#t()}#n(t){if(t.newState==="open")this.#i();else this.#s()}async#i(){if(this.triggerEl?.setAttribute("aria-expanded","true"),this.needsJsPositioning)await this.#r();if(this.keyboardValue)this.#l()}#s(){this.triggerEl?.setAttribute("aria-expanded","false"),this.#t()}#o(){return this.menuEl?.matches(":popover-open")??!1}#t(){if(this.stopAutoUpdate?.(),this.stopAutoUpdate=null,this.keyHandler)this.menuEl?.removeEventListener("keydown",this.keyHandler),this.keyHandler=null}async#r(){if(!this.triggerEl)return;let t;try{t=await import("@floating-ui/dom")}catch(i){console.warn("[daisy-dropdown] @floating-ui/dom is not available; the popover will open unpositioned. Pin it in your import map to enable the legacy-browser positioning fallback.",i);return}if(!this.#o())return;let{computePosition:e,autoUpdate:n,flip:s,shift:o,offset:r}=t;this.menuEl.style.position="fixed",this.menuEl.style.margin="0",this.stopAutoUpdate=n(this.triggerEl,this.menuEl,()=>{e(this.triggerEl,this.menuEl,{placement:this.#a(),middleware:[r(h),s(),o({padding:8})]}).then(({x:i,y:a})=>{Object.assign(this.menuEl.style,{left:`${i}px`,top:`${a}px`})})})}#a(){let t=this.menuEl.classList,e=t.contains("dropdown-top")?"top":t.contains("dropdown-left")?"left":t.contains("dropdown-right")?"right":"bottom",n=t.contains("dropdown-end")?"-end":t.contains("dropdown-center")?"":"-start";return e==="left"||e==="right"?e:`${e}${n}`}#l(){this.keyHandler=(t)=>this.#h(t),this.menuEl.addEventListener("keydown",this.keyHandler),this.#e()[0]?.focus()}#h(t){let e=this.#e();if(e.length===0)return;let n=e.indexOf(document.activeElement);switch(t.key){case"ArrowDown":t.preventDefault(),e[(n+1+e.length)%e.length].focus();break;case"ArrowUp":t.preventDefault(),e[(n-1+e.length)%e.length].focus();break;case"Home":t.preventDefault(),e[0].focus();break;case"End":t.preventDefault(),e[e.length-1].focus();break}}#e(){return Array.from(this.menuEl.querySelectorAll(d))}#d(){return typeof CSS<"u"&&typeof CSS.supports==="function"&&CSS.supports("anchor-name: --x")}}export{p as default};
|
|
2
|
+
|
|
3
|
+
//# debugId=E14AFCB07E09B55864756E2164756E21
|
|
4
|
+
//# sourceMappingURL=daisy_dropdown_controller.min.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["daisy_dropdown_controller.js"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { Controller } from \"@hotwired/stimulus\"\n\n// daisy-dropdown — OPT-IN enhancement for the :popover Dropdown variant.\n//\n// The native Popover API already handles open/toggle, light-dismiss (outside\n// click), Escape, and focus return — so this controller deliberately does NOT\n// re-implement any of that. Its only jobs are:\n//\n// 1. A positioning fallback for browsers WITHOUT CSS anchor positioning\n// (Safari < 26, Firefox < 147). On modern browsers it does nothing and\n// fetches no extra JavaScript. The fallback lazily imports @floating-ui/dom,\n// which the HOST app must pin (see README) — if it is missing the menu still\n// opens (native popover), just unpositioned, and a console warning is logged.\n// 2. Optional roving keyboard navigation over the menu's focusable items\n// (enable with data-daisy-dropdown-keyboard-value=\"true\").\n//\n// State is driven entirely by the popover's native `toggle` event; there is no\n// internal open flag, no document-level listeners, and no display toggling.\nconst OFFSET = 4\n\n// Focusable, interactive menu items. Prefers explicit role=\"menuitem\", but falls\n// back to the links/buttons DaisyUI menus actually render (<li><a>…</a></li>), so\n// keyboard nav works on stock markup without forcing roles onto caller content.\nconst ITEM_SELECTOR = '[role=\"menuitem\"]:not([aria-disabled=\"true\"]), a[href]:not([aria-disabled=\"true\"]), button:not([disabled])'\n\nexport default class extends Controller {\n static targets = [\"trigger\", \"menu\"]\n static values = { keyboard: { type: Boolean, default: false } }\n\n connect() {\n this.menuEl = this.hasMenuTarget ? this.menuTarget : this.element.querySelector(\"[popover]\")\n this.triggerEl = this.hasTriggerTarget ? this.triggerTarget : this.element.querySelector(\"[popovertarget]\")\n if (!this.menuEl) return\n\n this.needsJsPositioning = !this.#supportsAnchorPositioning()\n this.onToggle = (event) => this.#handleToggle(event)\n this.menuEl.addEventListener(\"toggle\", this.onToggle)\n }\n\n disconnect() {\n this.menuEl?.removeEventListener(\"toggle\", this.onToggle)\n this.#teardownOpen()\n }\n\n #handleToggle(event) {\n if (event.newState === \"open\") this.#onOpen()\n else this.#onClose()\n }\n\n async #onOpen() {\n // The native Popover API does not mirror open state onto the invoker, so\n // sync aria-expanded ourselves for assistive tech.\n this.triggerEl?.setAttribute(\"aria-expanded\", \"true\")\n if (this.needsJsPositioning) await this.#position()\n if (this.keyboardValue) this.#bindKeyboard()\n }\n\n #onClose() {\n this.triggerEl?.setAttribute(\"aria-expanded\", \"false\")\n this.#teardownOpen()\n // Do NOT restore focus — the native popover already returns focus to the trigger.\n }\n\n #isOpen() {\n return this.menuEl?.matches(\":popover-open\") ?? false\n }\n\n #teardownOpen() {\n this.stopAutoUpdate?.()\n this.stopAutoUpdate = null\n if (this.keyHandler) {\n this.menuEl?.removeEventListener(\"keydown\", this.keyHandler)\n this.keyHandler = null\n }\n }\n\n // --- positioning fallback (only when CSS anchor positioning is unavailable) ---\n\n async #position() {\n if (!this.triggerEl) return\n\n // Lazy + feature-gated, so modern browsers never fetch the positioning lib.\n // The host app is responsible for pinning @floating-ui/dom; if it is absent\n // we degrade gracefully (native popover still opens, just unpositioned).\n let floatingUi\n try {\n floatingUi = await import(\"@floating-ui/dom\")\n } catch (error) {\n console.warn(\n \"[daisy-dropdown] @floating-ui/dom is not available; the popover will open unpositioned. \" +\n \"Pin it in your import map to enable the legacy-browser positioning fallback.\",\n error,\n )\n return\n }\n\n // A fast open→close during the dynamic import may have already closed us;\n // bail rather than position (and leak an autoUpdate on) a closed menu.\n if (!this.#isOpen()) return\n\n const { computePosition, autoUpdate, flip, shift, offset } = floatingUi\n\n this.menuEl.style.position = \"fixed\"\n this.menuEl.style.margin = \"0\"\n\n this.stopAutoUpdate = autoUpdate(this.triggerEl, this.menuEl, () => {\n computePosition(this.triggerEl, this.menuEl, {\n placement: this.#placement(),\n middleware: [offset(OFFSET), flip(), shift({ padding: 8 })],\n }).then(({ x, y }) => {\n Object.assign(this.menuEl.style, { left: `${x}px`, top: `${y}px` })\n })\n })\n }\n\n // Derive Floating UI placement from DaisyUI's dropdown-* classes on the menu.\n #placement() {\n const cl = this.menuEl.classList\n const side = cl.contains(\"dropdown-top\")\n ? \"top\"\n : cl.contains(\"dropdown-left\")\n ? \"left\"\n : cl.contains(\"dropdown-right\")\n ? \"right\"\n : \"bottom\"\n const align = cl.contains(\"dropdown-end\") ? \"-end\" : cl.contains(\"dropdown-center\") ? \"\" : \"-start\"\n return (side === \"left\" || side === \"right\") ? side : `${side}${align}`\n }\n\n // --- optional roving keyboard navigation ---\n\n #bindKeyboard() {\n this.keyHandler = (event) => this.#onKeydown(event)\n this.menuEl.addEventListener(\"keydown\", this.keyHandler)\n this.#items()[0]?.focus()\n }\n\n #onKeydown(event) {\n const items = this.#items()\n if (items.length === 0) return\n const index = items.indexOf(document.activeElement)\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault()\n items[(index + 1 + items.length) % items.length].focus()\n break\n case \"ArrowUp\":\n event.preventDefault()\n items[(index - 1 + items.length) % items.length].focus()\n break\n case \"Home\":\n event.preventDefault()\n items[0].focus()\n break\n case \"End\":\n event.preventDefault()\n items[items.length - 1].focus()\n break\n }\n }\n\n #items() {\n return Array.from(this.menuEl.querySelectorAll(ITEM_SELECTOR))\n }\n\n #supportsAnchorPositioning() {\n return typeof CSS !== \"undefined\" && typeof CSS.supports === \"function\" && CSS.supports(\"anchor-name: --x\")\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "E14AFCB07E09B55864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{Controller as y}from"@hotwired/stimulus";var a=8,c=8,g=10,d=4,f='button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])',b={top:"bottom",bottom:"top",left:"right",right:"left"};class T extends y{static targets=["content","arrow"];static values={placement:{type:String,default:"top"}};connect(){if(!this.hasContentTarget)return;if(this.originalMaxWidth=this.contentTarget.style.maxWidth,this.onPointerEnter=()=>this.show(),this.onPointerLeave=()=>this.#m(),this.onFocusIn=()=>this.show(),this.onFocusOut=(t)=>this.#l(t),this.onKeydown=(t)=>this.#u(t),this.onGeometryChange=()=>this.#i(),this.element.addEventListener("pointerenter",this.onPointerEnter),this.element.addEventListener("pointerleave",this.onPointerLeave),this.element.addEventListener("focusin",this.onFocusIn),this.element.addEventListener("focusout",this.onFocusOut),this.#p(),this.element.classList.contains("tooltip-open"))this.show()}disconnect(){if(this.element.removeEventListener("pointerenter",this.onPointerEnter),this.element.removeEventListener("pointerleave",this.onPointerLeave),this.element.removeEventListener("focusin",this.onFocusIn),this.element.removeEventListener("focusout",this.onFocusOut),this.#e(),this.#g(),this.hasContentTarget)this.contentTarget.style.maxWidth=this.originalMaxWidth}show(){if(!this.hasContentTarget||this.#t())return;this.contentTarget.style.visibility="hidden",this.contentTarget.showPopover(),this.#h(),this.#i()}hide(){if(this.#t())this.contentTarget.hidePopover();this.contentTarget.style.visibility="hidden",this.#e()}#h(){window.addEventListener("resize",this.onGeometryChange),window.addEventListener("scroll",this.onGeometryChange,!0),window.addEventListener("keydown",this.onKeydown),window.visualViewport?.addEventListener("resize",this.onGeometryChange),window.visualViewport?.addEventListener("scroll",this.onGeometryChange)}#e(){cancelAnimationFrame(this.frame),window.removeEventListener("resize",this.onGeometryChange),window.removeEventListener("scroll",this.onGeometryChange,!0),window.removeEventListener("keydown",this.onKeydown),window.visualViewport?.removeEventListener("resize",this.onGeometryChange),window.visualViewport?.removeEventListener("scroll",this.onGeometryChange)}#i(){cancelAnimationFrame(this.frame),this.frame=requestAnimationFrame(()=>this.#r())}#r(){if(!this.#t())return;let t=this.#d(),e=Math.max(0,t.width-a*2);this.contentTarget.style.maxWidth=this.originalMaxWidth;let i=Number.parseFloat(getComputedStyle(this.contentTarget).maxWidth),n=Number.isFinite(i)?Math.min(i,e):e;this.contentTarget.style.maxWidth=`${n}px`;let o=this.element.getBoundingClientRect(),h=this.contentTarget.getBoundingClientRect(),r=this.#n(this.placementValue,o,h),s=b[this.placementValue]||"bottom",l=this.#n(s,o,h),m=this.#s(l,h,t)<this.#s(r,h,t),w=m?s:this.placementValue,v=m?l:r,{x:u,y:p}=this.#a(v,h,t);Object.assign(this.contentTarget.style,{left:`${u}px`,top:`${p}px`,visibility:"visible"}),this.#c(w,o,h,u,p)}#n(t,e,i){let n=e.left+e.width/2,o=e.top+e.height/2;switch(t){case"bottom":return{x:n-i.width/2,y:e.bottom+c};case"left":return{x:e.left-i.width-c,y:o-i.height/2};case"right":return{x:e.right+c,y:o-i.height/2};default:return{x:n-i.width/2,y:e.top-i.height-c}}}#s({x:t,y:e},i,n){let o=n.left+a,h=n.left+n.width-a,r=n.top+a,s=n.top+n.height-a;return Math.max(0,o-t)+Math.max(0,t+i.width-h)+Math.max(0,r-e)+Math.max(0,e+i.height-s)}#a({x:t,y:e},i,n){let o=n.left+a,h=n.left+n.width-a,r=n.top+a,s=n.top+n.height-a;return{x:i.width>h-o?o:Math.min(Math.max(t,o),h-i.width),y:i.height>s-r?r:Math.min(Math.max(e,r),s-i.height)}}#c(t,e,i,n,o){if(!this.hasArrowTarget)return;let h=e.left+e.width/2-n,r=e.top+e.height/2-o,s=this.arrowTarget.style;if(Object.assign(s,{inset:"auto",transform:"none"}),t==="top"||t==="bottom"){if(s.left=`${this.#o(h-g/2,4,i.width-g-4)}px`,t==="top")s.bottom=`${-d}px`;else s.top=`${-d}px`,s.transform="rotate(180deg)";return}if(s.top=`${this.#o(r-d/2,4,i.height-d-4)}px`,t==="left")s.right="-7px",s.transform="rotate(-90deg)";else s.left="-7px",s.transform="rotate(90deg)"}#d(){let t=window.visualViewport;return{left:t?.offsetLeft??0,top:t?.offsetTop??0,width:t?.width??window.innerWidth,height:t?.height??window.innerHeight}}#l(t){if(!this.element.contains(t.relatedTarget))this.hide()}#m(){if(!this.element.matches(":focus-within"))this.hide()}#u(t){if(t.key!=="Escape")return;this.hide()}#p(){let t=this.element.matches(f)?this.element:this.element.querySelector(f);if(!t||!this.contentTarget.id)return;this.describedTrigger=t,this.previousDescription=t.getAttribute("aria-describedby");let e=this.previousDescription?.split(/\s+/).filter(Boolean)||[];t.setAttribute("aria-describedby",[...new Set([...e,this.contentTarget.id])].join(" "))}#g(){if(!this.describedTrigger)return;if(this.previousDescription===null)this.describedTrigger.removeAttribute("aria-describedby");else this.describedTrigger.setAttribute("aria-describedby",this.previousDescription)}#t(){return this.hasContentTarget&&this.contentTarget.matches(":popover-open")}#o(t,e,i){return Math.min(Math.max(t,e),Math.max(e,i))}}export{T as default};
|
|
2
|
+
|
|
3
|
+
//# debugId=5716AAA30AA53C0864756E2164756E21
|
|
4
|
+
//# sourceMappingURL=daisy_tooltip_controller.min.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["daisy_tooltip_controller.js"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { Controller } from \"@hotwired/stimulus\"\n\nconst VIEWPORT_PADDING = 8\nconst GAP = 8\nconst ARROW_WIDTH = 10\nconst ARROW_HEIGHT = 4\nconst FOCUSABLE_SELECTOR = 'button, a[href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\nconst OPPOSITE = { top: \"bottom\", bottom: \"top\", left: \"right\", right: \"left\" }\n\n// daisy-tooltip — positions a :popover Tooltip in the browser top layer.\n//\n// The Popover API escapes clipping ancestors. This controller supplies the\n// hover/focus lifecycle expected of a tooltip, then flips and shifts the real\n// tooltip element only while it is open so it remains inside the visual\n// viewport. No global listeners remain attached while the tooltip is closed.\nexport default class extends Controller {\n static targets = [\"content\", \"arrow\"]\n static values = { placement: { type: String, default: \"top\" } }\n\n connect() {\n if (!this.hasContentTarget) return\n\n this.originalMaxWidth = this.contentTarget.style.maxWidth\n this.onPointerEnter = () => this.show()\n this.onPointerLeave = () => this.#hideUnlessFocused()\n this.onFocusIn = () => this.show()\n this.onFocusOut = (event) => this.#handleFocusOut(event)\n this.onKeydown = (event) => this.#handleKeydown(event)\n this.onGeometryChange = () => this.#schedulePosition()\n\n this.element.addEventListener(\"pointerenter\", this.onPointerEnter)\n this.element.addEventListener(\"pointerleave\", this.onPointerLeave)\n this.element.addEventListener(\"focusin\", this.onFocusIn)\n this.element.addEventListener(\"focusout\", this.onFocusOut)\n this.#describeTrigger()\n\n if (this.element.classList.contains(\"tooltip-open\")) this.show()\n }\n\n disconnect() {\n this.element.removeEventListener(\"pointerenter\", this.onPointerEnter)\n this.element.removeEventListener(\"pointerleave\", this.onPointerLeave)\n this.element.removeEventListener(\"focusin\", this.onFocusIn)\n this.element.removeEventListener(\"focusout\", this.onFocusOut)\n this.#teardownOpen()\n this.#restoreDescription()\n if (this.hasContentTarget) this.contentTarget.style.maxWidth = this.originalMaxWidth\n }\n\n show() {\n if (!this.hasContentTarget || this.#isOpen()) return\n\n this.contentTarget.style.visibility = \"hidden\"\n this.contentTarget.showPopover()\n this.#setupOpen()\n this.#schedulePosition()\n }\n\n hide() {\n if (this.#isOpen()) this.contentTarget.hidePopover()\n this.contentTarget.style.visibility = \"hidden\"\n this.#teardownOpen()\n }\n\n #setupOpen() {\n window.addEventListener(\"resize\", this.onGeometryChange)\n window.addEventListener(\"scroll\", this.onGeometryChange, true)\n window.addEventListener(\"keydown\", this.onKeydown)\n window.visualViewport?.addEventListener(\"resize\", this.onGeometryChange)\n window.visualViewport?.addEventListener(\"scroll\", this.onGeometryChange)\n }\n\n #teardownOpen() {\n cancelAnimationFrame(this.frame)\n window.removeEventListener(\"resize\", this.onGeometryChange)\n window.removeEventListener(\"scroll\", this.onGeometryChange, true)\n window.removeEventListener(\"keydown\", this.onKeydown)\n window.visualViewport?.removeEventListener(\"resize\", this.onGeometryChange)\n window.visualViewport?.removeEventListener(\"scroll\", this.onGeometryChange)\n }\n\n #schedulePosition() {\n cancelAnimationFrame(this.frame)\n this.frame = requestAnimationFrame(() => this.#position())\n }\n\n #position() {\n if (!this.#isOpen()) return\n\n const viewport = this.#viewport()\n const availableWidth = Math.max(0, viewport.width - VIEWPORT_PADDING * 2)\n this.contentTarget.style.maxWidth = this.originalMaxWidth\n const computedMaxWidth = Number.parseFloat(getComputedStyle(this.contentTarget).maxWidth)\n const maxWidth = Number.isFinite(computedMaxWidth) ? Math.min(computedMaxWidth, availableWidth) : availableWidth\n this.contentTarget.style.maxWidth = `${maxWidth}px`\n\n const trigger = this.element.getBoundingClientRect()\n const tooltip = this.contentTarget.getBoundingClientRect()\n const preferred = this.#coordinates(this.placementValue, trigger, tooltip)\n const oppositePlacement = OPPOSITE[this.placementValue] || \"bottom\"\n const opposite = this.#coordinates(oppositePlacement, trigger, tooltip)\n const useOpposite = this.#overflow(opposite, tooltip, viewport) < this.#overflow(preferred, tooltip, viewport)\n const placement = useOpposite ? oppositePlacement : this.placementValue\n const coordinates = useOpposite ? opposite : preferred\n const { x, y } = this.#shift(coordinates, tooltip, viewport)\n\n Object.assign(this.contentTarget.style, { left: `${x}px`, top: `${y}px`, visibility: \"visible\" })\n this.#positionArrow(placement, trigger, tooltip, x, y)\n }\n\n #coordinates(placement, trigger, tooltip) {\n const centerX = trigger.left + trigger.width / 2\n const centerY = trigger.top + trigger.height / 2\n\n switch (placement) {\n case \"bottom\":\n return { x: centerX - tooltip.width / 2, y: trigger.bottom + GAP }\n case \"left\":\n return { x: trigger.left - tooltip.width - GAP, y: centerY - tooltip.height / 2 }\n case \"right\":\n return { x: trigger.right + GAP, y: centerY - tooltip.height / 2 }\n default:\n return { x: centerX - tooltip.width / 2, y: trigger.top - tooltip.height - GAP }\n }\n }\n\n #overflow({ x, y }, tooltip, viewport) {\n const left = viewport.left + VIEWPORT_PADDING\n const right = viewport.left + viewport.width - VIEWPORT_PADDING\n const top = viewport.top + VIEWPORT_PADDING\n const bottom = viewport.top + viewport.height - VIEWPORT_PADDING\n\n return (\n Math.max(0, left - x) +\n Math.max(0, x + tooltip.width - right) +\n Math.max(0, top - y) +\n Math.max(0, y + tooltip.height - bottom)\n )\n }\n\n #shift({ x, y }, tooltip, viewport) {\n const left = viewport.left + VIEWPORT_PADDING\n const right = viewport.left + viewport.width - VIEWPORT_PADDING\n const top = viewport.top + VIEWPORT_PADDING\n const bottom = viewport.top + viewport.height - VIEWPORT_PADDING\n\n return {\n x: tooltip.width > right - left ? left : Math.min(Math.max(x, left), right - tooltip.width),\n y: tooltip.height > bottom - top ? top : Math.min(Math.max(y, top), bottom - tooltip.height),\n }\n }\n\n #positionArrow(placement, trigger, tooltip, x, y) {\n if (!this.hasArrowTarget) return\n\n const centerX = trigger.left + trigger.width / 2 - x\n const centerY = trigger.top + trigger.height / 2 - y\n const arrow = this.arrowTarget.style\n Object.assign(arrow, { inset: \"auto\", transform: \"none\" })\n\n if (placement === \"top\" || placement === \"bottom\") {\n arrow.left = `${this.#clamp(centerX - ARROW_WIDTH / 2, 4, tooltip.width - ARROW_WIDTH - 4)}px`\n if (placement === \"top\") arrow.bottom = `${-ARROW_HEIGHT}px`\n else {\n arrow.top = `${-ARROW_HEIGHT}px`\n arrow.transform = \"rotate(180deg)\"\n }\n return\n }\n\n arrow.top = `${this.#clamp(centerY - ARROW_HEIGHT / 2, 4, tooltip.height - ARROW_HEIGHT - 4)}px`\n if (placement === \"left\") {\n arrow.right = \"-7px\"\n arrow.transform = \"rotate(-90deg)\"\n } else {\n arrow.left = \"-7px\"\n arrow.transform = \"rotate(90deg)\"\n }\n }\n\n #viewport() {\n const visualViewport = window.visualViewport\n return {\n left: visualViewport?.offsetLeft ?? 0,\n top: visualViewport?.offsetTop ?? 0,\n width: visualViewport?.width ?? window.innerWidth,\n height: visualViewport?.height ?? window.innerHeight,\n }\n }\n\n #handleFocusOut(event) {\n if (!this.element.contains(event.relatedTarget)) this.hide()\n }\n\n #hideUnlessFocused() {\n if (!this.element.matches(\":focus-within\")) this.hide()\n }\n\n #handleKeydown(event) {\n if (event.key !== \"Escape\") return\n\n this.hide()\n }\n\n #describeTrigger() {\n const trigger = this.element.matches(FOCUSABLE_SELECTOR)\n ? this.element\n : this.element.querySelector(FOCUSABLE_SELECTOR)\n if (!trigger || !this.contentTarget.id) return\n\n this.describedTrigger = trigger\n this.previousDescription = trigger.getAttribute(\"aria-describedby\")\n const descriptions = this.previousDescription?.split(/\\s+/).filter(Boolean) || []\n trigger.setAttribute(\"aria-describedby\", [...new Set([...descriptions, this.contentTarget.id])].join(\" \"))\n }\n\n #restoreDescription() {\n if (!this.describedTrigger) return\n\n if (this.previousDescription === null) this.describedTrigger.removeAttribute(\"aria-describedby\")\n else this.describedTrigger.setAttribute(\"aria-describedby\", this.previousDescription)\n }\n\n #isOpen() {\n return this.hasContentTarget && this.contentTarget.matches(\":popover-open\")\n }\n\n #clamp(value, minimum, maximum) {\n return Math.min(Math.max(value, minimum), Math.max(minimum, maximum))\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "5716AAA30AA53C0864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
data/config/importmap.rb
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
# // app/javascript/controllers/index.js
|
|
8
8
|
# import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
|
|
9
9
|
# lazyLoadControllersFrom("daisy_ui/controllers", application)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
#
|
|
11
|
+
# Each pin resolves to the minified build (`rake build:js`), whose linked
|
|
12
|
+
# sourcemap shows the readable source in devtools. Pinned one by one, not with
|
|
13
|
+
# pin_all_from: the directory also holds the .min.js twins, which would become
|
|
14
|
+
# pins of their own. Not preloaded: a lazily registered controller is fetched
|
|
15
|
+
# the first time its data-controller appears, so a page without one skips it.
|
|
16
|
+
pin "daisy_ui/controllers/daisy_dropdown_controller",
|
|
17
|
+
to: "daisy_ui/controllers/daisy_dropdown_controller.min.js",
|
|
18
|
+
preload: false
|
|
19
|
+
pin "daisy_ui/controllers/daisy_tooltip_controller",
|
|
20
|
+
to: "daisy_ui/controllers/daisy_tooltip_controller.min.js",
|
|
21
|
+
preload: false
|
data/lib/daisy_ui/base.rb
CHANGED
|
@@ -119,9 +119,19 @@ module DaisyUI
|
|
|
119
119
|
}.freeze
|
|
120
120
|
|
|
121
121
|
class << self
|
|
122
|
-
attr_writer :component_class, :modifier_groups
|
|
122
|
+
attr_writer :component_class, :modifier_groups, :flags
|
|
123
123
|
attr_accessor :modifiers
|
|
124
124
|
|
|
125
|
+
# Positional symbols a component handles itself (e.g. Dropdown's
|
|
126
|
+
# :popover): accepted as modifiers, but they emit no class.
|
|
127
|
+
def flags
|
|
128
|
+
@flags ||= []
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def register_flags(*names)
|
|
132
|
+
self.flags = flags | names
|
|
133
|
+
end
|
|
134
|
+
|
|
125
135
|
# { group => [modifier, ...] }: modifiers in one group are alternatives
|
|
126
136
|
# (sizes, colors, ...), so ClassMerge keeps only the last one.
|
|
127
137
|
def modifier_groups
|
|
@@ -145,6 +155,7 @@ module DaisyUI
|
|
|
145
155
|
super
|
|
146
156
|
subclass.modifiers = (modifiers || {}).dup
|
|
147
157
|
subclass.modifier_groups = modifier_groups.transform_values(&:dup)
|
|
158
|
+
subclass.flags = flags.dup
|
|
148
159
|
# Inherit component_class if it was explicitly set
|
|
149
160
|
return unless instance_variable_defined?(:@component_class)
|
|
150
161
|
|
|
@@ -188,6 +199,7 @@ module DaisyUI
|
|
|
188
199
|
# Store all keyword arguments (class, data, aria, etc.)
|
|
189
200
|
# This will be processed by `classes` and `attributes` methods
|
|
190
201
|
@options = options
|
|
202
|
+
validate_modifiers
|
|
191
203
|
super()
|
|
192
204
|
end
|
|
193
205
|
|
|
@@ -277,11 +289,41 @@ module DaisyUI
|
|
|
277
289
|
{
|
|
278
290
|
skeleton: "skeleton",
|
|
279
291
|
**(self.class.modifiers || {}),
|
|
280
|
-
**
|
|
292
|
+
**configured_modifiers,
|
|
281
293
|
**DaisyUI.configuration.modifiers.for(component: nil)
|
|
282
294
|
}
|
|
283
295
|
end
|
|
284
296
|
|
|
297
|
+
# Modifiers added with `config.modifiers.add(component:)` for this class or
|
|
298
|
+
# any DaisyUI ancestor; the closest class wins.
|
|
299
|
+
def configured_modifiers
|
|
300
|
+
self.class.ancestors.take_while { |klass| klass != Base }.reverse.reduce({}) do |result, klass|
|
|
301
|
+
result.merge(DaisyUI.configuration.modifiers.for(component: klass))
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Reports positional modifiers that are neither in modifier_map nor a
|
|
306
|
+
# registered flag, and responsive ones not in modifier_map (a flag has no
|
|
307
|
+
# class to prefix). See Configuration#unknown_modifiers.
|
|
308
|
+
def validate_modifiers
|
|
309
|
+
responsive = (options[:responsive]&.values || []).flat_map { |mods| Array(mods) }
|
|
310
|
+
return if modifiers.empty? && responsive.empty?
|
|
311
|
+
|
|
312
|
+
map_keys = modifier_map.keys
|
|
313
|
+
report_unknown_modifiers(modifiers, map_keys | self.class.flags)
|
|
314
|
+
report_unknown_modifiers(responsive, map_keys)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# For sub-component methods with their own modifiers, e.g. Chat#bubble:
|
|
318
|
+
# `report_unknown_modifiers(modifiers, BUBBLE_MODIFIERS.keys, part: :bubble)`.
|
|
319
|
+
def report_unknown_modifiers(requested, known, part: nil)
|
|
320
|
+
requested.each do |modifier|
|
|
321
|
+
next if !modifier.is_a?(Symbol) || known.include?(modifier)
|
|
322
|
+
|
|
323
|
+
DaisyUI.configuration.report_unknown_modifier(component: self.class, modifier:, known:, part:)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
285
327
|
def extract_boolean_modifiers(options)
|
|
286
328
|
modifier_keys = modifier_map.keys
|
|
287
329
|
boolean_mods = []
|
data/lib/daisy_ui/button.rb
CHANGED
|
@@ -6,6 +6,8 @@ module DaisyUI
|
|
|
6
6
|
class Button < Base
|
|
7
7
|
self.component_class = :btn
|
|
8
8
|
|
|
9
|
+
FORM_CONTROLS = %i[button input].freeze
|
|
10
|
+
|
|
9
11
|
def initialize(*, as: :button, modal: nil, **)
|
|
10
12
|
super(*, as:, **)
|
|
11
13
|
@modal = modal
|
|
@@ -29,6 +31,16 @@ module DaisyUI
|
|
|
29
31
|
|
|
30
32
|
attr_reader :modal
|
|
31
33
|
|
|
34
|
+
# `disabled` is consumed as a modifier (btn-disabled), which only blocks
|
|
35
|
+
# pointer events. Form controls also need the attribute so keyboard
|
|
36
|
+
# activation and implicit form submission are blocked too.
|
|
37
|
+
def attributes
|
|
38
|
+
attrs = super
|
|
39
|
+
return attrs unless FORM_CONTROLS.include?(as.to_sym) && modifiers.include?(:disabled)
|
|
40
|
+
|
|
41
|
+
attrs.merge(disabled: true)
|
|
42
|
+
end
|
|
43
|
+
|
|
32
44
|
register_modifiers(
|
|
33
45
|
style: {
|
|
34
46
|
# "sm:btn-ghost"
|
data/lib/daisy_ui/chat.rb
CHANGED
|
@@ -108,6 +108,7 @@ module DaisyUI
|
|
|
108
108
|
private
|
|
109
109
|
|
|
110
110
|
def build_bubble_classes(modifiers, options)
|
|
111
|
+
report_unknown_modifiers(modifiers, BUBBLE_MODIFIERS.keys, part: :bubble)
|
|
111
112
|
modifier_classes = modifiers.filter_map { |mod| apply_prefix(BUBBLE_MODIFIERS[mod]) }
|
|
112
113
|
merge_classes(apply_prefix("chat-bubble"), *modifier_classes, options.delete(:class))
|
|
113
114
|
end
|
data/lib/daisy_ui/checkbox.rb
CHANGED
|
@@ -52,10 +52,49 @@ module DaisyUI
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
class Configuration
|
|
55
|
+
UNKNOWN_MODIFIER_MODES = %i[raise warn ignore].freeze
|
|
56
|
+
REPORTED_UNKNOWN_MODIFIERS_LIMIT = 1_000
|
|
57
|
+
|
|
55
58
|
attr_reader :prefix
|
|
56
59
|
|
|
57
60
|
def initialize
|
|
58
61
|
@prefix = nil
|
|
62
|
+
@reported_unknown_modifiers = Set.new
|
|
63
|
+
@reported_unknown_modifiers_lock = Mutex.new
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# What a component does with a positional modifier it does not know:
|
|
67
|
+
# :raise, :warn (once per component and modifier), :ignore, or a callable
|
|
68
|
+
# that receives the DaisyUI::UnknownModifierError (once per component and
|
|
69
|
+
# modifier), e.g. `->(error) { Appsignal.report_error(error) }`.
|
|
70
|
+
#
|
|
71
|
+
# Defaults to :raise under Rails in development and test, :warn otherwise.
|
|
72
|
+
def unknown_modifiers
|
|
73
|
+
@unknown_modifiers || default_unknown_modifiers
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def unknown_modifiers=(mode)
|
|
77
|
+
valid = mode.nil? || mode.respond_to?(:call) || UNKNOWN_MODIFIER_MODES.include?(mode)
|
|
78
|
+
raise ArgumentError, "unknown_modifiers must be :raise, :warn, :ignore or a callable, got #{mode.inspect}" unless valid
|
|
79
|
+
|
|
80
|
+
@unknown_modifiers = mode
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @api private Builds the error only when it will be raised or reported.
|
|
84
|
+
def report_unknown_modifier(component:, modifier:, known:, part: nil)
|
|
85
|
+
mode = unknown_modifiers
|
|
86
|
+
return if mode == :ignore
|
|
87
|
+
|
|
88
|
+
error = -> { UnknownModifierError.new(component:, modifier:, known:, part:) }
|
|
89
|
+
raise error.call if mode == :raise
|
|
90
|
+
return unless first_report?([component, part, modifier])
|
|
91
|
+
|
|
92
|
+
mode == :warn ? warn("[DaisyUI] #{error.call.message}") : mode.call(error.call)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
def reset_reported_unknown_modifiers!
|
|
97
|
+
@reported_unknown_modifiers_lock.synchronize { @reported_unknown_modifiers.clear }
|
|
59
98
|
end
|
|
60
99
|
|
|
61
100
|
def prefix=(prefix)
|
|
@@ -70,6 +109,26 @@ module DaisyUI
|
|
|
70
109
|
def modifiers
|
|
71
110
|
@modifiers ||= Modifiers.new
|
|
72
111
|
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
# Remembers up to REPORTED_UNKNOWN_MODIFIERS_LIMIT keys, then starts over,
|
|
116
|
+
# so modifiers built from user input cannot grow it without bound.
|
|
117
|
+
def first_report?(key)
|
|
118
|
+
@reported_unknown_modifiers_lock.synchronize do
|
|
119
|
+
return false if @reported_unknown_modifiers.include?(key)
|
|
120
|
+
|
|
121
|
+
@reported_unknown_modifiers.clear if @reported_unknown_modifiers.size >= REPORTED_UNKNOWN_MODIFIERS_LIMIT
|
|
122
|
+
@reported_unknown_modifiers.add(key)
|
|
123
|
+
true
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def default_unknown_modifiers
|
|
128
|
+
return :warn unless defined?(::Rails) && ::Rails.respond_to?(:env)
|
|
129
|
+
|
|
130
|
+
%w[development test].include?(::Rails.env.to_s) ? :raise : :warn
|
|
131
|
+
end
|
|
73
132
|
end
|
|
74
133
|
end
|
|
75
134
|
end
|
data/lib/daisy_ui/dropdown.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "securerandom"
|
|
|
5
5
|
module DaisyUI
|
|
6
6
|
class Dropdown < Base
|
|
7
7
|
self.component_class = :dropdown
|
|
8
|
+
register_flags :popover, :tap_to_close
|
|
8
9
|
|
|
9
10
|
# Placement modifiers that, in :popover mode, must live on the popover
|
|
10
11
|
# element itself (DaisyUI's `position-area` rule is on `.dropdown`).
|
data/lib/daisy_ui/engine.rb
CHANGED
|
@@ -4,24 +4,34 @@ module DaisyUI
|
|
|
4
4
|
# Optional Rails integration. Loaded only when Rails::Engine is defined (see
|
|
5
5
|
# lib/daisy_ui.rb), so the gem stays a plain Phlex library outside Rails.
|
|
6
6
|
#
|
|
7
|
-
# It exposes the gem's bundled
|
|
8
|
-
#
|
|
9
|
-
# *
|
|
10
|
-
#
|
|
11
|
-
# manual pin;
|
|
7
|
+
# It exposes the gem's bundled Stimulus controllers (`daisy-dropdown`,
|
|
8
|
+
# `daisy-tooltip`) to the host app:
|
|
9
|
+
# * adds config/importmap.rb to importmap-rails' paths, which pins each
|
|
10
|
+
# `daisy_ui/controllers/<name>` to its minified build (no manual pin);
|
|
12
11
|
# * appends the JS dir to the asset load path so Propshaft/Sprockets serve
|
|
13
|
-
# the
|
|
12
|
+
# the .min.js and its sourcemap (Sprockets also needs them precompiled).
|
|
14
13
|
#
|
|
15
|
-
# The
|
|
16
|
-
# true)`), so
|
|
14
|
+
# The controllers are opt-in at the markup level (`Dropdown(:popover, stimulus:
|
|
15
|
+
# true)`, `Tooltip(:popover)`) and not preloaded, so a page that never uses
|
|
16
|
+
# one never fetches it.
|
|
17
17
|
# Asset-only engine: no isolate_namespace, since it exposes no routes, models,
|
|
18
18
|
# or helpers — only static JavaScript — and namespacing would risk surprises in
|
|
19
19
|
# the host app for no benefit.
|
|
20
20
|
class Engine < ::Rails::Engine
|
|
21
21
|
JAVASCRIPT_PATH = root.join("app/javascript")
|
|
22
|
+
# The minified controllers config/importmap.rb pins, and their sourcemaps.
|
|
23
|
+
MINIFIED_ASSETS = Dir[JAVASCRIPT_PATH.join("daisy_ui/controllers/*.min.js{,.map}").to_s]
|
|
24
|
+
.map { |path| Pathname.new(path).relative_path_from(JAVASCRIPT_PATH).to_s }.sort.freeze
|
|
22
25
|
|
|
23
26
|
initializer "daisy_ui.assets" do |app|
|
|
24
|
-
|
|
27
|
+
next unless app.config.respond_to?(:assets)
|
|
28
|
+
|
|
29
|
+
app.config.assets.paths << JAVASCRIPT_PATH.to_s
|
|
30
|
+
# Sprockets compiles only what is listed; Propshaft compiles every file in
|
|
31
|
+
# paths. The list is nil unless Sprockets or another engine created it,
|
|
32
|
+
# and OrderedOptions answers respond_to? for any name, so check the value.
|
|
33
|
+
precompile = app.config.assets.precompile
|
|
34
|
+
precompile.concat(MINIFIED_ASSETS) if precompile.is_a?(Array)
|
|
25
35
|
end
|
|
26
36
|
|
|
27
37
|
initializer "daisy_ui.importmap", before: "importmap" do |app|
|
data/lib/daisy_ui/indicator.rb
CHANGED
|
@@ -19,6 +19,7 @@ module DaisyUI
|
|
|
19
19
|
private
|
|
20
20
|
|
|
21
21
|
def build_item_classes(item_modifiers, options)
|
|
22
|
+
report_unknown_modifiers(item_modifiers, modifier_map.keys, part: :item)
|
|
22
23
|
# Start with base class
|
|
23
24
|
base = apply_prefix("indicator-item")
|
|
24
25
|
# Add modifier classes from position modifiers
|
data/lib/daisy_ui/modal.rb
CHANGED
data/lib/daisy_ui/steps.rb
CHANGED
|
@@ -27,6 +27,7 @@ module DaisyUI
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
29
|
def build_step_classes(step_modifiers, options)
|
|
30
|
+
report_unknown_modifiers(step_modifiers, step_modifier_map.keys, part: :step)
|
|
30
31
|
base = apply_prefix("step")
|
|
31
32
|
mod_classes = step_modifiers.filter_map { |m| apply_prefix(step_modifier_map[m]) }
|
|
32
33
|
custom = options.delete(:class)
|
data/lib/daisy_ui/sub_menu.rb
CHANGED
data/lib/daisy_ui/tab.rb
CHANGED
|
@@ -36,5 +36,11 @@ module DaisyUI
|
|
|
36
36
|
div role: :tabpanel, class: content_classes, **options, &block
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Tab only dispatches: TabWithContent or TabWithoutContent validates the
|
|
43
|
+
# modifiers when it is built.
|
|
44
|
+
def validate_modifiers; end
|
|
39
45
|
end
|
|
40
46
|
end
|
data/lib/daisy_ui/tooltip.rb
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "did_you_mean"
|
|
4
|
+
|
|
5
|
+
module DaisyUI
|
|
6
|
+
# Raised (or warned, or reported) when a component is given a positional
|
|
7
|
+
# modifier it does not know. See `DaisyUI.configuration.unknown_modifiers`.
|
|
8
|
+
class UnknownModifierError < ArgumentError
|
|
9
|
+
attr_reader :component, :modifier, :part
|
|
10
|
+
|
|
11
|
+
# `part` names the sub-component method (e.g. :bubble for Chat#bubble).
|
|
12
|
+
def initialize(component:, modifier:, known:, part: nil)
|
|
13
|
+
@component = component
|
|
14
|
+
@modifier = modifier
|
|
15
|
+
@part = part
|
|
16
|
+
super(build_message(known))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def build_message(known)
|
|
22
|
+
name = component.name || component.inspect
|
|
23
|
+
name = "#{name}##{part}" if part
|
|
24
|
+
message = "#{name} has no modifier #{modifier.inspect}"
|
|
25
|
+
suggestions = DidYouMean::SpellChecker.new(dictionary: known.map(&:to_s)).correct(modifier.to_s)
|
|
26
|
+
return message if suggestions.empty?
|
|
27
|
+
|
|
28
|
+
"#{message}. Did you mean? #{suggestions.map { |s| ":#{s}" }.join(', ')}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/daisy_ui/updated_at.rb
CHANGED
data/lib/daisy_ui/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: daisyui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -63,7 +63,11 @@ files:
|
|
|
63
63
|
- LICENSE.txt
|
|
64
64
|
- README.md
|
|
65
65
|
- app/javascript/daisy_ui/controllers/daisy_dropdown_controller.js
|
|
66
|
+
- app/javascript/daisy_ui/controllers/daisy_dropdown_controller.min.js
|
|
67
|
+
- app/javascript/daisy_ui/controllers/daisy_dropdown_controller.min.js.map
|
|
66
68
|
- app/javascript/daisy_ui/controllers/daisy_tooltip_controller.js
|
|
69
|
+
- app/javascript/daisy_ui/controllers/daisy_tooltip_controller.min.js
|
|
70
|
+
- app/javascript/daisy_ui/controllers/daisy_tooltip_controller.min.js.map
|
|
67
71
|
- config/importmap.rb
|
|
68
72
|
- exe/daisyui-mcp
|
|
69
73
|
- lib/daisy_ui.rb
|
|
@@ -157,6 +161,7 @@ files:
|
|
|
157
161
|
- lib/daisy_ui/toast.rb
|
|
158
162
|
- lib/daisy_ui/toggle.rb
|
|
159
163
|
- lib/daisy_ui/tooltip.rb
|
|
164
|
+
- lib/daisy_ui/unknown_modifier_error.rb
|
|
160
165
|
- lib/daisy_ui/updated_at.rb
|
|
161
166
|
- lib/daisy_ui/validator.rb
|
|
162
167
|
- lib/daisy_ui/version.rb
|