npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fenestron-ui

v0.0.2-alpha

Published

🗔 A UI component framework for building idiomatic Windows 10 apps with Vue and Electron. Part of the Fenestron Project.

Downloads

3

Readme

Fenestron UI

A Vue UI component framework inspired by Microsoft's XAML and UWP.

Fenestron UI is part of the Fenestron project. Fenestron helps developers build idiomatic Windows 10 apps with Electron and Vue. Idiomatic apps don't use native technologies but look and feel like they do.

If you are looking for a component framework to build web apps with then these probably aren't the droids you are looking for. If you are looking to build Windows 10 apps with Electron then we suggest you head on over to the Fenestron repo.

  1. Why Fenestron
  2. Work In Progress
  3. Getting Started
  4. Layout and Sizing
  5. Components
  6. Included Libraries

0. Why Fenestron?

There are many reasons to consider Fenestron for your Windows 10 app. Here are a few:

  • Deliver idiomatic apps on Windows 10 that users will love.
  • Helps you respect your users' preferences and choices while still using the technology you know and are invested in
  • Provides easy access to Windows 10 app integration features like light and dark theme support and the user's preferred accent color
  • Provides a collection of powerful UI components like TabView, Pivot, and GroupedListView that come with native Windows 10 behavior built in
  • Easy access to Fluent Design features such as Microsoft designed iconography and typeography and depth through subtle use of blur, transparency, and shadow

If you are interested in a longer-form treatment of the "why" question we have a document that goes into greater depth.

1. Work In Progress

If this text is still here then Fenestron UI is an active work in progress not yet released or fit for use. You are welcome to kick the tires and read the sticker on the window but we're not quite ready to pull out of the lot yet.

2. Getting Started

This library is under active development and isn't yet avialable on NPM. The only reason to use it right now is to work on the library, so the current instalation advice is to checkout the repo and the add fenestron-ui as a dependcy of a test app for development purposes:

Development>git clone [email protected]:adamrdrew/fenestron-ui.git
Development>cd exampleapp
Development\exampleapp>npm install --save file:../fenestron-ui

Then in your test app's entrypoint register FenestronUI. The following example includes an electron call to get the dark mode status and set the correct option in FenestronUI:

//Get UI prefs
import electron from "electron"
store.state.theme.accentColor = electron.remote.systemPreferences.getAccentColor()
store.state.theme.isDarkMode  = electron.remote.systemPreferences.isDarkMode()

//Start FenestronUI
import FenestronUI from "fenestron-ui"
Vue.use(FenestronUI,{
  darkMode: store.state.theme.isDarkMode
})

From there you should now be able to serve out your Electron app and use FenestronUI components.

Note: This method will reload your test app when you make changes to FenestronUI and you don't need to update.

3. Layout and Sizing

Fenestron's layout and sizing systems are inspired by XAML's rather than HTML's. This means that containers size themselves to fill up the maximum amount of space available to them rather than sizing themselves based on their content. A <div> with a few words in it will only take up the space required for its content. In Fenestron a BlurPanel or LayerPanel etc will size itelf to occupy the entire spce it finds itself in. This means that for the vast majority of layouts you will not have to think about manually sizing or positioning elements. When you do need to size and position elements, such as with the grid templates in Grid or the foregroud panel layout of LayerPanel you will do so through props on the components rather than CSS. This approach makes constructing familiar UIs simple and largely hassle free.

4. Components

4.1. TitleBar

Creates a window title bar that matches the Windows 10 UWP app style. Presents the expected native behavior including maximize/unmaximize, close, minimize, and window dragging. Has a space for the app's title, and implements a back button. Icons are taken from Office Fabric ensuring they represent Microsoft designs.

The back button uses the browser engine's History system, just like a back button in a browser. Combined with Vue Router and a clean SPA app design this can do a great job emulating native back button behavior.

To use TitleBar make sure frame is set to false in the window's BrowserWindow constructor in your Electron app's main process setup code. I also recommend you disable the in app menu bar:

  win = new BrowserWindow({ 
    width: 1000, 
    height: 800,
    frame: false,
    webPreferences: {
      nodeIntegration: true
  } })

  win.setMenuBarVisibility(false)

4.1.1. Example

<template lang="pug">
.frame
  TitleBar(:back-button="true", title="Title Bar Example")
  Grid
</template>

TitleBar Example

4.1.2. Props

| Name | Type | Default | Description |-|-|-|- | title | String | "My Great App" | The title displayed | back-button | Boolean | false | Display the back button in the upper-right

4.2. Grid

Grid based layout component. Designed to make it easy to create app page layouts.

4.2.1. Example

Use 2 grids to create a layout with a header, content section, and footer with the content divided into left and right panels.

<template lang="pug">
Grid(rows="3" :row-definitions='["4em", "auto", "4em"]')
  div(style="background-color: pink;") Header
  Grid(columns="2", :column-definitions='["10em", "auto"]')
    div(style="background-color: red;") Left Panel
    div(style="background-color: orange;") Right Panel
  div(style="background-color: purple;") Footer
</template>

Grid Example

4.2.2. Props

| Name | Type | Default | Description |-|-|-|- | width-percent | Number | 100 | Width of the grid in percent of parent | height-percent | Number | 100 | Height of the grid in percent of parent | columns | Number | 0 | Number of columns | rows | Number | 0 | Number of rows | column-definitions | Array | [] | Array of column width definitions. Use any valid CSS units. Use auto to fill available remaining space. em is recomended. | row-definitions | Array | [] | Array of row height definitions. Use any valid CSS units. Use auto to fill available remaining space. em is recomended.

4.3. StackPanel

A panel that arranges items in row, horizontally or vertically. Has the ability to scroll, wrap, and to combine the two. Can be styled with a background color and an opacity.

<template lang="pug">
Grid(rows="3" :row-definitions='["4em", "auto", "4em"]')
  div(style="background-color: pink;") Header
  Grid(columns="2", :column-definitions='["10em", "auto"]')
    StackPanel(:scroll="true")
      div(v-for="i in new Array(100)") Entry
    StackPanel(:scroll="true", orientation="horizontal", wrap="wrap")
      div(v-for="i in new Array(100)") Entry
  div(style="background-color: purple;") Footer
</template>

StackPanel Example

4.3.1. Props

| Name | Type | Default | Description |-|-|-|- | width-percent | Number | 100 | Width of the grid in percent of parent | height-percent | Number | 100 | Height of the grid in percent of parent | scroll | Boolean | false | Turns on scrolling. Scrolling direction will depend on the orientation | orientation | String | "vertical" | Controls the direction that the children stack, wrap, and scroll | wrap | String | "no-wrap" | Defines the content wrap behavior. Values are valid flex-box wrap values: wrap, no-wrap, wrap-reverse | opacity | Number | 1.0 | The opacity of the panle | rgb | String | 255,255,255 | The background color for the panel, defined in RGB. Must be a string of RGB values, comma seperated.

4.4. SplitView

Provides the familiar Windows 10 SplitView "side bar" seen in apps like Groove, Mail, Todo, etc. SplitView comes with a "hamburger" toggle button already wired-up.

SplitView has two display modes: inline and overlay. Inline mode shows the content and pane next to each other with the content resizing and reflowing based on the pane being opened or closed. Overlay displays the pane over the content with the content size and flow never changing. Inline mode has a solid background color. Overlay mode implements acrylic, showing the content underneath and bluring it generously. Inline is the default.

4.4.1. Inline Example

<template lang="pug">
  SplitView
    template(v-slot:pane="slotProps")
      PaneButton(:pane-open="slotProps.paneOpen", icon="DietPlanNotebook", title="Notebooks")
      PaneButton(:pane-open="slotProps.paneOpen", icon="BulkUpload", title="Lists")
    template(v-slot:content)
      ...
</template>

SplitView Example

4.4.2. Overlay Example

<template lang="pug">
  SplitView(display-mode="overlay")
    template(v-slot:pane="slotProps")
      PaneButton(:pane-open="slotProps.paneOpen", icon="DietPlanNotebook", title="Notebooks")
      PaneButton(:pane-open="slotProps.paneOpen", icon="BulkUpload", title="Lists")
    template(v-slot:content)
      ...
</template>

SplitView Example

SplitView provides 2 named slots: pane and content for the left and right sides respectively. The pane slot exposes the pane's open/closed status via the pane-open slot prop. The open and closed sizes of the pane are controlled by the open-width and compact-width props, with defaults set to match Windows 10 defaults.

The content slot can be used for the rest of the content in your app. For a SPA experience putting router-view in the content slot works very well. The pane-open slot prop is also passed to content just in case you need it to adjust something else in your UI.

The pane slot works best with controls designed for it such as PaneButton.

The content slot will scroll overflowed content.

Setting the title-bar-offset prop to true will offset the content and pane areas by the height of the titlebar. This is implemented inconsistently in Microsoft's Windows 10 apps: some apps have a title bar offset from the content, and in others the two run into eachother. SplitView gives you the option to do what you want.

The "hamburger" toggle button can be disabled with the toggle-button boolean prop, if your app calls for that.

4.4.3. Props

| Name | Type | Default | Description |-|-|-|- | open-width | String | 23em | How wide the pane is when open | closed-width | String | 3.2em | How wide the pane is when closed | pane-background | String | #e1e1e1 | Pane background color | title-bar-offset | Boolean | false | Offset the top by the size of the TitleBar | display-mode | String | "inline" | "inline" puts pane and content side by side. "overlay" puts pane over content with acrylic. | toggle-button | Boolean | true | If false the toggle button will be disabled

4.5. PaneButton

Button designed to work in the SplitView's Pane. Has an icon on the left and can be collapsed to only show the icon. Emits a click event.

PaneButton provides easy VueRouter navigation via the navigate prop. Setting navigate to the name of a route will cause the button to perform a VueRouter navigate to the named route on click. When the navigate prop is in use there's no need to manually set the active prop - the button will activate itself when the active route name matches its navigate prop. A PaneButton with a navigate prop will still emit click when clicked.

<template lang="pug">
  SplitView
    template(v-slot:pane="slotProps")
      PaneButton(:use-accent-color="true", :pane-open="slotProps.paneOpen", icon="DietPlanNotebook", title="Demo Page 1", navigate="pageOne")
      PaneButton(:pane-open="slotProps.paneOpen", icon="BulkUpload", title="Demo Page 2", navigate="pageTwo")
    template(v-slot:content)
      h1 Right
</template>

SplitView Example

4.5.1. Props

| Name | Type | Default | Description |-|-|-|- | icon | String | "GlobalNavButton" | The icon for the button. Appears on the left and remains even when the pane is closed. Takes a Fabric Icon name with no prefixes. | title | String | "Button" | The label to the right of the icon. Hidden when the pane is closed. | pane-open | Boolean| true | Sets whether the pane that contains the button is open or closed. When open the icon and the title are visible; when closed just the icon. | highlight | Boolean| false | Background is highlighted on mouseover | navigate | String | NoNavigationConfigured | Must be the name property of a route. If set to a value other than the default will navgiate to the provded route name. If configured the PaneButton will have its active state set automatically. | use-accent-color | Boolean | false | If true the icon will use the system's accent color | active | Boolean | false | If true show a thin border on the left side using the system's accent color. Useful if indicating that the currently loaded page is the page for this button. You do not need to set this manually if you are using navigate

4.6. BlurPanel

An extended StackPanel that implements an acryllic look. Blurs the background of whatever is behind it and adds a subtle drop shadow. Has all of the same props as StackPanel because it inherits from it but extends it with a new blurSize prop.

| Name | Type | Default | Description |-|-|-|- | width-percent | Number | 100 | Width of the grid in percent of parent | height-percent | Number | 100 | Height of the grid in percent of parent | scroll | Boolean | false | Turns on scrolling. Scrolling direction will depend on the orientation | orientation | String | "vertical" | Controls the direction that the children stack, wrap, and scroll | wrap | String | "no-wrap" | Defines the content wrap behavior. Values are valid flex-box wrap values: wrap, no-wrap, wrap-reverse | opacity | Number | 1.0 | The opacity of the panle | rgb | String | 255,255,255 | The background color for the panel, defined in RGB. Must be a string of RGB values, comma seperated. | blur-size | Number | 1 | The blur size in pixels

<template lang="pug">
LayerPanel(fg-height="5em", fg-top="0")
    template(v-slot:background)
        StackPanel(:scroll="true")
            div(v-for="i in new Array(100)") Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot. Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot.
    template(v-slot:foreground)
        BlurPanel(rgb="255,255,255", :blur-size="3", :opacity="0.5") This is BlurPanel Content
</template>

BlurPanel Example

4.7. LayerPanel

Allows you to arrange content in 2 layers: foreground and background. Useful when you want to display some content over some other content. Effective when combined with BlurPanel so that the background layer is seen through and blurred by the foreground layer.

The background layer is essentially a pass-through; placing content in the background layer is like placing it in an empty 100%x100% div with no whitespace. There are no special props or options for the background layer. You probably want to put some more capable container in it such as a Grid or StackPanel.

The foreground layer's layout and size can be controlled via the 6 fg-* props that LayerPanel exposes. Most of the time you are not going to want to obscure the entire background with the entire foreground. Instead, you are likely going to want to create panels that obscure only parts of the background. These positioning props let you set the size and position of the foreground layer so that it doesn't obscure the entire background.

Layers are added as named slots: foreground and background.

LayerPanel is very flexible. You can create all kinds of different layouts with it, so multiple examples are shown.

4.7.1. Example: Modal-Like

LayerPanel(fg-height="10em", fg-left="5em", fg-right="5em", fg-width="auto", fg-top="30%")
    template(v-slot:background)
        StackPanel(:scroll="true")
            div(v-for="i in new Array(100)") Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot. Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot.
    template(v-slot:foreground)
        BlurPanel(rgb="30,30,30", :blur-size="3", :opacity="0.5") This is BlurPanel Content

LayerPanel Example

4.7.2. Example: Top Panel

LayerPanel(fg-height="5em")
    template(v-slot:background)
        StackPanel(:scroll="true")
            div(v-for="i in new Array(100)") Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot. Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot.
    template(v-slot:foreground)
        BlurPanel(rgb="30,30,30", :blur-size="3", :opacity="0.5") This is BlurPanel Content

LayerPanel Example

4.7.3. Example: Bottom Panel

LayerPanel(fg-height="5em", fg-bottom="0", fg-top="auto")
    template(v-slot:background)
        StackPanel(:scroll="true")
            div(v-for="i in new Array(100)") Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot. Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot.
    template(v-slot:foreground)
        BlurPanel(rgb="30,30,30", :blur-size="3", :opacity="0.5") This is BlurPanel Content

LayerPanel Example

4.7.4. Example: Picture In Picture

LayerPanel(fg-width="10em", fg-height="10em", fg-top="2em", fg-left="auto", fg-right="2em")
    template(v-slot:background)
        StackPanel(:scroll="true")
            div(v-for="i in new Array(100)") Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot. Lorem ipsum dolomor sit amet. Quesio esto el etat sinder quadrato abat domot.
    template(v-slot:foreground)
        BlurPanel(rgb="30,30,30", :blur-size="3", :opacity="0.5") This is BlurPanel Content

LayerPanel Example

4.7.5. Props

All LayerPanel props have the same type and work the same way so they're listed here as a list instead of a table. All of the fg-* props are strings and all accept valid CSS values for measurements, such as exact units, percentages, and even calc.

  • fg-width
  • fg-height
  • fg-top
  • fg-bottom
  • fg-right
  • fg-left

4.8. Pivot, PivotItem, & PivotContent

Shows child content containers in a tabbed interface. Pivot works together with PivotItem and PivotContent to provide a complete tabbed UI without any implementation code.

Pivot gives you a lot of functionality without requiring that you write any methods, but it requires that you nest and arrange your components correctly. All of the PivotItems need to go in the items slot and the PivotContents need to go in the content slot. Each PivotContent and PivotItem pair need to share the same tab-id prop value.

We recommend that you nest components in your PivotContents rather than build UI directly in them, but the example below shows both approaches.

4.8.1. Example:

Pivot(default-tab-id="allEmail")
    template(#items)
        PivotItem(label="All", tab-id="allEmail")
        PivotItem(label="Unread", tab-id="unreadEmail")
        PivotItem(label="Junk", tab-id="junkEmail") 
    template(#content)
        PivotContent(tab-id="allEmail")
            h1 Hi from adam
        PivotContent(tab-id="unreadEmail")
            DemoPageTwo
        PivotContent(tab-id="junkEmail")
            DemoPageOne

Pivot Example

4.8.2. Props

| Component | Prop | Type | Default | Description |-|-|-|-|-| Pivot | default-tab-id | String | N/A | The ID of the default tab to show PivotItem | label | String | N/A | The text to display in the tab button |PivotItem| tab-id | String | N/A | The ID of the tab to show. Maps to a corresponding PivotContent |PivotContent| tab-id | String | N/A | The ID of the tab content. Maps to corresponding PivotItem

4.9. ParallexPanel

A panel with a background image and a parallax scrolling effect. A blur effect can optionally be applied to the background image.

<template lang="pug">
Grid(rows="2" :row-definitions='["20em", "auto"]')
    ParallaxPanel(:background-image="image", :blur="true", blurSize="5px", )
        h3(v-for="i in new Array(50)", style="color: white;") Lorem ipsum dolomor sit amet.Lorem ipsum dolomor sit amet.Lorem ipsum dolomor sit 
    ParallaxPanel(:background-image="imageTwo", :blur="false" )
        h3(v-for="i in new Array(50)", style="color: white;") Lorem ipsum dolomor sit amet.Lorem ipsum dolomor sit amet.Lorem ipsum dolomor sit 
</template>
<script>
export default {
    data:() => ({
        image: require("@/assets/ziggy.jpg"),
        imageTwo: require("@/assets/raleigh.jpg")
    }) 
}
</script>

ParallaxPanel Example

4.10. GroupedList

Displays a list of items with headers. Both the headers and items are clickable. Clicking an item emits an item-clicked event. Clicking a header hides the items and shows a grid of just the headers. Clicking on a header in the grid returns to the list and scrolls to the items for the header clicked.

GroupedList needs to be provided with both the items and the headers, and doesn't do anything fancy like sort your headers or items, or determine headers from the data. It will display whatever you provide it in whatever order your provided it. This means you will likely need to do some work to get your list data in the right format for GroupedList but it ensures you have total control over the process and makes the headers very flexible. You could use numbers, letters, states or provinces, codes, or whatever else your data requires.

GroupedList uses transition animations for showing and hiding both the list and the header grid. The default transition animations consist of interlocking zooms in and out. You can change these via props, or omit them all-together.

Items are rendered via the default slot. The default slot has fallback content, meaning if you provide no slot template a simple list of headings will be rendered. Using the slot you can create any kind of custom UI you want for your list. The slot has item bound as a slot prop. This means that the item is in scope for your slot template so you can access any data on the items you want from your template.

4.10.1. Example:

<template>
  Grid(rows="2", columns="1", :row-definitions='["10%", "90%"]')
      StackPanel
          h1 Artists
      GroupedListView(:items-source="artists", @item-click="itemClicked")
<template>

| Items | Headers | |- |- | | GroupedListView Example 1 | GroupedListView Example 2 |

4.10.2. Example with Item Template

<template>
    Grid(rows="2", columns="1", :row-definitions='["10%", "90%"]')
        StackPanel
            h1 Artists
        GroupedListView(v-slot="slotProps", :items-source="artists", @item-click="itemClicked")
            div(style="border-style: solid; border-width: 1px; border-color: pink; color: white;") {{ slotProps.item.content }}
<template>

GroupedListView Example 2 |

4.11. CommandBar, AppBarButton, & AppBarSeperator

CommandBar presents important and useful commands to the user. It is displayed as a bar filled with buttons that sticks to either the top or the bottom of the current page with the option to auto-hide. CommandBar exposes two named slots for primary and secondary commands. Primary commands are pulled to the left of the bar and secondary on the right. You could technically put any component in CommandBar's slots but Fenestron comes with components that are intended for use in CommandBar: AppBarButton and AppBarSeperator. Because these components are almost always used together they'll all be detailed in this section.

4.11.1. Example:

The following example shows two CommandBar configurations: top of the screen with auto-hide and bottom of the screen withiout auto-hide. A Grid splits the view into 2 sections each with a different CommandBar.

Grid(rows="2", columns="1", :row-definitions='["50%","50%"]')
    StackPanel(:scroll="true")
        CommandBar(:auto-hide="true")
            template(#primary-commands)
                AppBarButton(title="Nope", icon="OfflineOneDriveParachuteDisabled")
        | {{ artistInfo }}
    StackPanel(:scroll="true")
        CommandBar(:bottom="true")
            template(#primary-commands)
                AppBarButton(title="Reload", icon="History", :show-title="false", @click="doSomething")
                AppBarButton(title="Update", icon="ArrangeBringForward", @click="click")
                AppBarButton(title="Config", icon="ATPLogo")
                AppBarSeperator
                AppBarButton(title="Drinks", icon="Coffee")
            template(#secondary-commands)
                AppBarButton(title="More", icon="ContextMenu")
        | {{ artistInfo }}

CommandBar Example 2

Notice that the top CommandBar in the example is collapsed - that is due to the auto-hide prop. If the user mouses-over the collapsed CommandBar it would expand to full size just like the bottom example. On mouse out it will collapse again after a delay.

What the images don't show is how CommandBar handles scrolling and content flow. The scroll and content flow behavior is complex and hard to describe; it is recomended that you try CommandBar out to get a better feel for how it works. If the CommandBar is on the top then content in the same view will start after the CommandBar. In the example above the top CommandBar doesn't obscure the content at all. However, when the user scorlls the containing view the content will pass behind the CommandBar while the CommandBar sticks in its position. This makes it behave exactly like a page or window border. This also means that if auto-hide is enabled and the containing view is scrolled all the way to the top then showing the CommandBar will reflow and move the other content in the view around it. However, if the user is scrolled at all then showing the CommandBar will simply place it over the content. When the CommandBar is on the bottom however, it always obscures the content behind it even when at the starting scroll position. This means that when you use CommandBar on the bottom you will need to leave some space on the end of your page content if the user needs to scroll all the way to the bottom. In most cases you shouldn't have to but you can use a Grid to place your CommandBar in a different container if you are having trouble with your content flow around CommandBar.

A final note: you cannot put multiple CommandBar instances in the same container. Notice in the example that we used Grid to cut the page in half to produce two seperate containers and then used those for our examples. If you attempt to use two ComamndBar instances in the same container you will have layout problems. If you need 2 CommandBar instances then use a grid to sequester the bottom CommandBar into its own container. It is not recomended that you use multiple CommandBars at the same time because it may be visually confusing to the user and send unclear signals about what is important and what isn't, but if your app calls for it you can do it.

It is recomended you use AppBarButton and AppBarSeperator for your CommandBar children. AppBarSeperator just draws a 1px thin line and has no props or slots. AppBarButton displays both text and an icon with the option to omit the text. Any Microsoft Fabric Icon class can be affixed via the icon slot.

AppBarButton emits a click event when clicked.

4.11.2. Props

| Component | Prop | Type | Default | Description |-|-|-|-|-| CommandBar | bottom | Boolean | false | Puts the command bar at the bottom of the container CommandBar | auto-hide | Boolean | false | Collapses the CommandBar automatically. Expands on mouse over. ComamndBar | hide-time-out | Number | 500 | Time in milliseconds to wait before collapsing the CommandBar if auto-hide is one AppBarButton | title | String | "Button" | The title that displays under the icon AppBarButton | icon | String | "BuildQueue" | A icon class from Microsoft Fabric Icons AppBarButtons | shot-title | Boolean | true | If false the icon will be displayed without text under it

4.11.3. CommandBar Slots

  • primary-commands - Slot for the most important buttons. Placed on the left side of the CommandBar
  • secondary-commands - Slot for the less important buttons. Placed on the right side of the CommandBar.

4.12. TabView

Presents a tabbed interface for switching between multiple different instances of a child component. TabView is a powerful component that provides many tab features including tab close buttons, drag and drop reordering, and two different background tab modes.

Due to TabView's richness its API is fairly complex. It presents many props and events and comes with a controller mixin. TabView also attempts to minimize the demands it puts on your data but at the cost of further (optional)configuraiton. If you wish to use TabView it is recommended that you read this entire TabView guide.

4.12.1. Data Model and Custom Item Properties

TabView requires an array of objects to iterate over and create tabs for. The item-source prop is used for this array; we refer to the objects in this array as "items". By default TabView looks for the following properties of your items: item.id, item.title, item.icon. The id and title properties are required, icon is optional.

However, to minimize the demands placed on your data model you can configure TabView to use different property names. The id-property, title-property, and icon-property String props can be used to change the properties of your items that TabView accesses:

<template lang="pug">
TabView(
    :item-source="articles", 
    id-property="guid",
    title-property="headline",
    icon-property="thumbnail", 
    ...)
</template>
<script>
export default {
    data:() => ({
        articles: [
            {guid: 1234, headline: "Example of custom item properties", thumbnail: "/public/thumb.png"}
        ]
    })
}
</script>

4.12.2. Selected Item ID v-model

TabView handles the work of showing and hiding the correct tab content based on the user's selection so you don't have to worry about doing that. However, it may still be useful for your code to know what item is active. You might want to send the selected ID to an API for recomendations of similar docs, or load associated data from another source by ID, etc. You can use v-model with TabView to keep a property on your component's data object updated with the ID of the selected item.

<template lang="pug">
TabView(:item-source="documents", v-model="activeDocumentID", ...)
</template>
<script>
export default {
    data: () => ({
        documents: [ ... ],
        activeDocumentID: ""
    })
}
</script>

4.12.3. Active vs Suspended Background Mode

In active-background mode tab content for tabs that aren't currently selected continue to run in the background. They aren't visible, but they are still open and doing whatever it is they should be doing. Anything the application is allowed to do active-background tabs can also do. However, any code they are running that is looking for DOM elements or DOM changes may fail or act in unexpected ways (see the background slot prop for info on how to handle this.)

In suspended-background mode tab content for tabs that aren't currently selected are removed from the component graph and deactived. They no longer execute and any state they were holding is lost. That last part is important. Anything that the component had in its local state that hasn't been comitted somewhere else will be lost. Because of this it is important make sure you save any content you may need to save before allowing the destroy operation to complete. You can either do this in the child component by implementing the beforeDestroy component life cycle hook, or in the parent component by handling the before-destroy event the child will emit just before it is destroyed.

Another important point to remember is that keys are required when using suspended-background mode. Keys are always a good idea when working with iteration generated components, but when working with suspended-background tabs they are required. If you do not implement keys you may observe strange behavior such as the states of multiple components seeming to "mix" or "blend" together.

A final background tab feature to be aware of is the background slot prop. You may want active background tabs but still want some features of your child component to turn off when in the background. You may want their state preserved and left open but you may want to suspend a long running operation for example. Your child component will be passed a background Boolean prop that you can guard activity behind.

4.12.4. Content Slot and Slot Props

TabView creates and manages the tabs but it also handles showing and hiding your tab content. Your tab content will be dislplayed in a child component that you provide. For example, if your TabView is used in a text editor you might have a child component that models and presents the UI for a single document. Your tab content child content must be slotted into the TabView component via the tab-content named slot.

The tab-content slot provides some props to your child component. This is how your child component will get access to its item, have its unique ID key assigned, and be informed if it has been backgrounded:

<template lang="pug">
TabView(:item-source="webpages", ...)
    template(v-slot:tab-content="slotProps")
        WebPage(
            v-model="slotProps.item", 
            :key="slotProps.item.id", 
            :background="slotProps.background")
</template>

4.12.5. Item Mutability

You may have noticed that our items are being handed down through props. This makes them immuatable in your child components. If you intend for your tab content children to be able to write to your items this could be a problem. There are a few different ways you can handle this.

We don't recommend that the item source array contain your full content objects. For example, if you are writing a tabbed word processor we wouldn't advice that your item source array be filled with all of your documents. Rather we suggest you use the item source array as a collection of IDs and then have your child components load and manage the data they need.

That said, for some smaller apps that may be overkill and you may want to use your item source array as your entire data model. For example, maybe in a todo or sticky note app. In such a case you can either implement custom events in your child components and have your parent listen for them, or similarly implement v-model in your child components and then v-model="slotProps.item" instead of :item="slotProps.item".

4.12.6. Drag and Drop Reordering

TabView provides drag and drop functionality for reordering tabs. Enabling drag and drop requires a combination of events and a mixin. Tab reordering updates your data model, so moving tabs around also moves aorund the order of entries in your array.

Fenestron includes the TabController mixin. You'll need to include this mixin in the component that houses your TabView. TabController saves you the hassle of implementing the required reodering code for the drag event handler; it doesn't do anything special, just a splice with the correct indexes based on the data transfer from the drag event.

Next, you'll need to make your TabView handle the drag-and-drop event and enable can-drag. The drag-and-drop event handler will call the dragAndDrop method provided by TabController and take two arguments, the event and your item colleciton. Along with that you need to enable drag and drop by setting can-drag to true. Example:

<template>
TabView(
    :item-source="webpages",
    :can-drag="true",
    @drag-and-drop="dragAndDrop($event, webpages)"
)
</template>
<script>
import TabController from `fenestron/TabController`
export default {
    mixins: [ TabController ],
    data: () => ({
        webpages: [ { ... } ... ]
    })
}
</script>

To be clear, you don't need to implement your own dragAndDrop method - it is provided by TabController. You just need to wire up the method to the event hander and provide it the event and your item colleciton.

4.12.7. Tab Close Button

Implementing tab close buttons is almost exactly like implementing drag and drop. You include the TabController mixin, enable can-close, and then wire up the tabCloseClicked method to the tab-close-clicked method, handing it $event and your item collection:

<template>
TabView(
    :item-source="webpages",
    :can-close="true",
    @tab-close-clicked="tabCloseClicked($event, webpages)"
)
</template>
<script>
import TabController from `fenestron/TabController`
export default {
    mixins: [ TabController ],
    data: () => ({
        webpages: [ { ... } ... ]
    })
}
</script>

TabView Example

Grid(rows="1", columns="2", :column-definitions="['50%', '50%']")
    StackPanel
        h1 Active Background Tabs
        p This demo shows tab content that remains active in the background. Start a counter and switch between tabs. The counter will keep running.
        TabView(
            :item-source="itemSelectionOne", 
            v-model="selectedActiveTabId", 
            :active-background="true",
            :canClose="true",
            :canDrag="true",
            @tab-close-clicked="tabCloseClicked($event, itemSelectionOne)"
            @drag-and-drop="dragAndDrop($event, itemSelectionOne)")
            template(v-slot:tab-content="slotProps")
                DemoTabContent(
                    :item="slotProps.item", 
                    :key="slotProps.item.id", 
                    :background="slotProps.background")
    StackPanel
        h1 Suspended Background Tabs
        p This demo shows tab content that is removed from the DOM when in the background. Any state or data not explicitely saved will be lost.
        TabView(:item-source="itemSelectionTwo", title-property="name", id-property="guid", v-model="selectedSuspendedTabID")
            template(v-slot:tab-content="slotProps")
                DemoTabContent(:item="slotProps.item", :key="slotProps.item.guid")

5. Included Libraries

Fenestron stands on the shoulders of many wonderful projects. Some of our dependencies are available to you, the library consumer, so they are listed here. You are free to use these in your own projects without including any additional dependencies:

  • Vue Electron - Provides access to the Electron APIs on your Vue instance.
  • Office UI Fabric Core - Provides us all of the benefit of Microsoft's design chops. Provides iconography and typography that give our apps a native look and feel.