summaryrefslogtreecommitdiff
path: root/docs/guide/ags/first-widgets.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/guide/ags/first-widgets.md')
-rw-r--r--docs/guide/ags/first-widgets.md401
1 files changed, 0 insertions, 401 deletions
diff --git a/docs/guide/ags/first-widgets.md b/docs/guide/ags/first-widgets.md
deleted file mode 100644
index c4c4436..0000000
--- a/docs/guide/ags/first-widgets.md
+++ /dev/null
@@ -1,401 +0,0 @@
-# First Widgets
-
-AGS is the predecessor of Astal, which was written purely in TypeScript and so only supported
-JavaScript/TypeScript. Now it serves as a scaffolding tool for Astal projects in TypeScript.
-While what made AGS what it is, is now part of the Astal project, for simplicity we will
-refer to the Astal TypeScript lib as AGS.
-
-:::tip
-If you are not familiar with the JavaScript syntax [MDN](https://developer.mozilla.org/en-US/)
-and [javascript.info](https://javascript.info/) have great references.
-:::
-
-## Getting Started
-
-Start by initializing a project
-
-```sh
-ags --init
-```
-
-then run `ags` in the terminal
-
-```sh
-ags
-```
-
-Done! You have now a custom written bar using Gtk.
-
-:::tip
-AGS will transpile every `.ts`, `.jsx` and `.tsx` files into regular javascript then
-it will bundle everything into a single javascript file which then GJS can execute.
-The bundler used is [esbuild](https://esbuild.github.io/).
-:::
-
-## Root of every shell component: Window
-
-Astal apps are composed of widgets. A widget is a piece of UI that has its own logic and style.
-A widget can be as small as a button or an entire bar.
-The top level widget is always a [Window](https://aylur.github.io/libastal/class.Window.html) which will hold all widgets.
-
-::: code-group
-
-```tsx [widget/Bar.tsx]
-function Bar(monitor = 0) {
- return <window className="Bar" monitor={monitor}>
- <box>Content of the widget</box>
- </window>
-}
-```
-
-:::
-
-::: code-group
-
-```ts [app.ts]
-import Bar from "./widget/Bar"
-
-App.start({
- main() {
- Bar(0)
- Bar(1) // instantiate for each monitor
- },
-})
-```
-
-:::
-
-## Creating and nesting widgets
-
-Widgets are JavaScript functions which return Gtk widgets,
-either by using JSX or using a widget constructor.
-
-:::code-group
-
-```tsx [MyButton.tsx]
-function MyButton(): JSX.Element {
- return <button onClicked="echo hello">
- Clicke Me!
- </button>
-}
-```
-
-```ts [MyButton.ts]
-import { Widget } from "astal"
-
-function MyButton(): Widget.Button {
- return Widget.Button({
- onClicked: "echo hello",
- label: "Click Me!",
- })
-}
-```
-
-:::
-
-:::info
-The only difference between the two is the return type.
-Using markup the return type is always `Gtk.Widget` (globally available as `JSX.Element`),
-while using constructors the return type is the type of the widget.
-It is rare to need the actual return type, so most if not all of the time, you can use markup.
-:::
-
-Now that you have declared `MyButton`, you can nest it into another component.
-
-```tsx
-function MyBar() {
- return <window>
- <box>
- Click The button
- <MyButton />
- </box>
- </window>
-}
-```
-
-Notice that widgets you defined start with a capital letter `<MyButton />`.
-Lowercase tags are builtin widgets, while capital letter is for custom widgets.
-
-## Displaying Data
-
-JSX lets you put markup into JavaScript.
-Curly braces let you “escape back” into JavaScript so that you can embed some variable
-from your code and display it.
-
-```tsx
-function MyWidget() {
- const label = "hello"
-
- return <button>{label}</button>
-}
-```
-
-You can also pass JavaScript to markup attributes
-
-```tsx
-function MyWidget() {
- const label = "hello"
-
- return <button label={label} />
-}
-```
-
-## Conditional Rendering
-
-You can use the same techniques as you use when writing regular JavaScript code.
-For example, you can use an if statement to conditionally include JSX:
-
-```tsx
-function MyWidget() {
- let content
-
- if (condition) {
- content = <True />
- } else {
- content = <False />
- }
-
- return <box>{content}</box>
-}
-```
-
-You can also inline a [conditional `?`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator) (ternary) expression.
-
-```tsx
-function MyWidget() {
- return <box>{condition ? <True /> : <False />}</box>
-}
-```
-
-When you don’t need the `else` branch, you can also use a shorter [logical && syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#short-circuit_evaluation):
-
-```tsx
-function MyWidget() {
- return <box>{condition && <True />}</box>
-}
-```
-
-:::info
-As you can guess from the above snippet, [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) values are not rendered.
-:::
-
-## Rendering lists
-
-You can use [`for` loops](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) or [array `map()` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
-
-```tsx
-function MyWidget() {
- const labels = [
- "label1"
- "label2"
- "label3"
- ]
-
- return <box>
- {labels.map(label => (
- <label label={label} />
- ))}
- </box>
-}
-```
-
-## Widget signal handlers
-
-You can respond to events by declaring event handler functions inside your widget:
-
-```tsx
-function MyButton() {
- function onClicked(self: Widget.Button, ...args) {
- console.log(self, "was clicked")
- }
-
- return <button onClicked={onClicked} />
-}
-```
-
-The handler can also be a string, which will get executed in a subprocess asynchronously.
-
-```tsx
-function MyButton() {
- return <button onClicked="echo hello" />
-}
-```
-
-:::info
-Attributes prefixed with `on` will connect to a `signal` of the widget.
-Their types are not generated, but written by hand, which means not all of them are typed.
-Refer to the Gtk and Astal docs to have a full list of them.
-:::
-
-## How properties are passed
-
-Using JSX, a custom widget will always have a single object as its parameter.
-
-```ts
-type Props = {
- myprop: string
- child?: JSX.Element // when only one child is passed
- children?: Array<JSX.Element> // when multiple children are passed
-}
-
-function MyWidget({ myprop, child, children }: Props) {
- //
-}
-```
-
-```tsx
-// child prop of MyWidget is the box
-return <MyWidget myprop="hello">
- <box />
-</MyWidget>
-```
-
-```tsx
-// children prop of MyWidget is [box, box, box]
-return <MyWidget myprop="hello">
- <box />
- <box />
- <box />
-</MyWidget>
-```
-
-## State management
-
-The state of widgets are handled with Bindings. A `Binding` lets you
-connect the state of one [GObject](https://docs.gtk.org/gobject/class.Object.html) to another, in our case it is used to
-rerender part of a widget based on the state of a `GObject`.
-A `GObject` can be a [Variable](./variable) or it can be from a [Library](../libraries/references).
-
-We use the `bind` function to create a `Binding` object from a `Variable` or
-a regular GObject and one of its properties.
-
-Here is an example of a Counter widget that uses a `Variable` as its state:
-
-```tsx
-import { Variable, bind } from "astal"
-
-function Counter() {
- const count = Variable(0)
-
- function increment() {
- count.set(count.get() + 1)
- }
-
- return <box>
- <label label={bind(count).as(num => num.toString())} />
- <button onClicked={increment}>
- Click to increment
- <button>
- </box>
-}
-```
-
-:::info
-Bindings have an `.as()` method which lets you transform the assigned value.
-In the case of a Label, its label property expects a string, so it needs to be
-turned to a string first.
-:::
-
-:::tip
-`Variables` have a shorthand for `bind(variable).as(transform)`
-
-```tsx
-const v = Variable(0)
-const transform = (v) => v.toString()
-
-return <box>
- {/* these two are equivalent */}
- <label label={bind(v).as(transform)} />
- <label label={v(transform)} />
-</box>
-```
-
-:::
-
-Here is an example of a battery percent label that binds the `percentage`
-property of the Battery object from the [Battery Library](/guide/libraries/battery):
-
-```tsx
-import Battery from "gi://AstalBattery"
-import { bind } from "astal"
-
-function BatteryPercentage() {
- const bat = Battery.get_default()
-
- return <label label={bind(bat, "percentage").as((p) => p * 100 + " %")} />
-}
-```
-
-## Dynamic children
-
-You can also use a `Binding` for `child` and `children` properties.
-
-```tsx
-const child = Variable(<box />)
-
-return <box>{child}</box>
-```
-
-```tsx
-const num = Variable(3)
-const range = (n) => [...Array(n).keys()]
-
-return <box>
- {num(n => range(n).map(i => (
- <button>
- {i.toString()}
- <button/>
- )))}
-<box>
-```
-
-:::warning
-Only bind children of the `box` or the `stack` widget. Gtk does not cleanup widgets by default,
-they have to be explicitly destroyed. The box widget is a special container that
-will implicitly call `.destroy()` on its removed child widgets.
-You can disable this behavior by setting the `noImplicityDestroy` property.
-:::
-
-:::info
-The above example destroys and recreates every widget in the list everytime
-the value of the `Variable` changes. There might be cases where you would
-want to [handle child creation and deletion](/guide/ags/faq#avoiding-unnecessary-re-rendering)
-yourself, because you don't want to lose the
-inner state of widgets that does not need to be recreated.
-:::
-
-When there is at least one `Binding` passed as a child, the `children`
-parameter will always be a flattened `Binding<Array<JSX.Element>>`
-
-```tsx
-function MyContainer({ children }) {
- // children is a Binding over an Array of widgets
-}
-
-return <MyContainer>
- <box />
- {num(n => range(n).map(i => (
- <button>
- {i.toString()}
- <button/>
- )))}
- [
- [
- <button />
- ]
- <button />
- ]
-</MyContainer>
-```
-
-:::info
-You can pass the followings as children:
-
-- widgets
-- deeply nested arrays of widgets
-- bindings of widgets,
-- bindings of deeply nested arrays of widgets
-
-[falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) values are not rendered and anything not from this list
-will be coerced into a string and rendered as a label
-:::