diff --git a/nixosModules/ags/v2/default.nix b/nixosModules/ags/v2/default.nix index a63a98f5..7199e422 100644 --- a/nixosModules/ags/v2/default.nix +++ b/nixosModules/ags/v2/default.nix @@ -50,6 +50,7 @@ self: { "skipLibCheck" = true; "checkJs" = true; "allowJs" = true; + "experimentalDecorators" = true; "jsx" = "react-jsx"; "jsxImportSource" = "${agsV2Packages.astal}/share/astal/gjs/src/jsx"; "paths" = { diff --git a/nixosModules/ags/v2/eslint.config.ts b/nixosModules/ags/v2/eslint.config.ts index 0a3eea06..fdfb502a 100644 --- a/nixosModules/ags/v2/eslint.config.ts +++ b/nixosModules/ags/v2/eslint.config.ts @@ -30,6 +30,9 @@ export default tseslint.config({ 'class-methods-use-this': 'off', '@stylistic/no-multiple-empty-lines': 'off', '@stylistic/jsx-indent-props': 'off', + 'no-use-before-define': 'off', + '@typescript-eslint/no-use-before-define': 'error', + '@stylistic/indent-binary-ops': 'off', // Pre-flat config '@typescript-eslint/no-unused-vars': [ @@ -64,12 +67,6 @@ export default tseslint.config({ ], }, ], - 'no-use-before-define': [ - 'error', - { - functions: false, - }, - ], 'block-scoped-var': [ 'error', ], diff --git a/nixosModules/ags/v2/widgets/misc/popup-window.tsx b/nixosModules/ags/v2/widgets/misc/popup-window.tsx new file mode 100644 index 00000000..9814d7d2 --- /dev/null +++ b/nixosModules/ags/v2/widgets/misc/popup-window.tsx @@ -0,0 +1,88 @@ +import { Astal, Binding, idle, Widget } from 'astal'; +import { register, property } from 'astal/gobject'; + +import AstalHyprland from 'gi://AstalHyprland?version=0.1'; +const Hyprland = AstalHyprland.get_default(); + +/* Types */ +type CloseType = 'none' | 'stay' | 'released' | 'clicked'; +type HyprTransition = 'slide' | 'slide top' | 'slide bottom' | 'slide left' | + 'slide right' | 'popin' | 'fade'; +type PopupCallback = (self: PopupWindow) => void; + +type PopupWindowProps = Widget.WindowProps & { + transition?: HyprTransition | Binding + close_on_unfocus?: CloseType | Binding + on_open?: PopupCallback + on_close?: PopupCallback +}; + + +@register() +class PopupWindow extends Widget.Window { + @property(String) + declare transition: HyprTransition | Binding; + + @property(String) + declare close_on_unfocus: CloseType | Binding; + + @property(Object) + declare on_open: PopupCallback; + + @property(Object) + declare on_close: PopupCallback; + + constructor({ + transition = 'fade', + close_on_unfocus = 'none', + on_open = () => { /**/ }, + on_close = () => { /**/ }, + + name, + visible = false, + layer = Astal.Layer.OVERLAY, + ...rest + }: PopupWindowProps) { + super({ + ...rest, + name, + namespace: `win-${name}`, + visible: false, + layer, + setup: () => idle(() => { + // Add way to make window open on startup + if (visible) { + this.visible = true; + } + }), + }); + + const setTransition = (_: PopupWindow, t: HyprTransition | Binding) => { + Hyprland.message_async( + `keyword layerrule animation ${t}, ${this.name}`, + () => { /**/ }, + ); + }; + + this.connect('notify::transition', setTransition); + + this.close_on_unfocus = close_on_unfocus; + this.transition = transition; + this.on_open = on_open; + this.on_close = on_close; + + this.connect('notify::visible', () => { + // Make sure we have the right animation + setTransition(this, this.transition); + + if (this.visible) { + this.on_open(this); + } + else { + this.on_close(this); + } + }); + }; +} + +export default (props: PopupWindowProps) => new PopupWindow(props);