组件布局

概述

当您在文档中引入交互组件时,您会希望以优化可读性和导航的方式进行布局。

当然,您可以通过多种方式引入交互性,从嵌入长篇文章的可视化到更接近应用程序/仪表板风格的布局。下面我们将涵盖这两种布局场景。

我们将从Observable JSShiny交互式文档中使用示例——如果您对某个示例使用的代码/语法不熟悉,只需关注包含布局标记而不是应用程序代码。

输入面板

如果您有多个输入,您可能希望将它们分组在一个输入面板中(带有panel: input选项的代码块或带有.panel-input类的div)。例如:

输入被分组在一个面板中,并通过向OJS代码单元格添加panel: inputlayout-ncol: 3选项以三列布局:

```{ojs}
//| panel: input
//| layout-ncol: 3

viewof ch = checkbox({
  title: "护照颜色:",
  options: [
    { value: "red", label: "红色" },
    { value: "green", label: "绿色" },
    { value: "blue", label: "蓝色" },
    { value: "black", label: "黑色" }
  ],
  value: ["red", "green", "blue", "black"],
  submit: false
})

viewof type = radio({
  title: "表示方式:",
  options: [
    { label: '护照', value: 'p' },
    { label: '圆形', value: 'c' }
  ],
  value: 'p'
})

viewof k = slider({
  title: "符号大小:",
  min: 1,
  max: 10,
  value: 3,
  step: 1
})
```

标签页面板

如果您希望允许用户在多个可视化之间切换,请使用标签页(带有.panel-tabset类的div)。为标签页中的每个标签包含一个标题(例如##)。

例如,这里有一个图表和数据,每个都在自己的标签中展示:

以下是创建标签页所使用的标记和代码:

::: {.panel-tabset}

## 图表

```{ojs}
Plot.rectY(data, 
  Plot.stackY(
    Plot.binX( 
      {y: "count"}, 
      {x: "body_mass_g", fill: "species", thresholds: 20})
    )
  ).plot({
    facet: {
      data,
      x: "sex"
    },
    marks: [Plot.frame()]
  })
```

## 数据

```{ojs}
Inputs.table(filtered)
```

:::

全页布局

默认情况下,Quarto文档将其内容居中于文档视口中,并且不超过大约900像素的最大宽度。这种行为是为了优化可读性,但对于应用程序布局,您通常希望占据整个页面。

为此,请添加page-layout: custom选项。例如:

format: 
  html:
    page-layout: custom

以下是一个占据浏览器全宽的Shiny应用程序示例:

您还会注意到,输入包含在一个侧边栏中——下一节将描述如何创建侧边栏。

侧边栏面板

侧边栏使用带有.panel-sidebar类的div创建。您可以使用Markdown div容器(如上面用于.panel-input所示),或者,如果侧边栏的全部内容由单个代码单元格创建,则通过向单元格添加panel: sidebar选项。

侧边栏面板应始终有一个相邻的带有.panel-fill.panel-center类的面板,它们将并排布局。前者(.panel-fill)将填充所有可用空间,后者(.panel-center)将在其内容周围留出一些水平边距。

例如,以下是上面显示的Shiny应用程序的用户界面部分的源代码:

---
title: "鸢尾花K-Means聚类"
format: 
  html:
    page-layout: custom
server: shiny
---

```{r}
#| panel: sidebar
vars <- setdiff(names(iris), "Species")
selectInput('xcol', 'X变量', vars)
selectInput('ycol', 'Y变量', vars, selected = vars[[2]])
numericInput('clusters', '聚类数量', 3, min = 1, max = 9)
```

```{r}
#| panel: fill
plotOutput('plot1')
```

panel: fill选项被添加到图表输出块中。如果您希望在面板内容周围留出一些水平边距,可以交替使用panel: center

向代码块添加panel选项是向其包含的div添加CSS类的简写(即它等同于用带有类(例如panel-fill)的div包围代码块)。 以下是使用带有 OJS 输入的侧边栏的示例:

为此,您可以使用以下代码:

```{ojs}
//| panel: sidebar

viewof myage = {
  const myage = select({
    title: "您想绘制哪个年龄段的图表?",
    options: ages,
    value: "80etplus"
  });
  return myage;
}

viewof pctvax = slider({
  title: '<br/>疫苗接种目标',
  description: '200% 表示每个人接种两剂疫苗',
  min: 50,
  max: 200,
  value: 200,
  step: 10,
  format: v => v + "%"
})

viewof overlay = radio({
  title: "分散圆圈",
  options: [{ label: '是', value: 'Y' }, { label: '否', value: 'N' }],
  value: 'N'
})

viewof label = radio({
  title: "部门编号",
  options: [{ label: '显示', value: 'Y' }, { label: '隐藏', value: 'N' }],
  value: 'N'
})
```

```{ojs}
//| panel: fill

(疫苗可视化代码)

```

面板布局

您可以使用包含 div 的 layout 属性将多个交互组件排列到面板中。例如,这里我们在第一行有一个主可视化,在第二行有两个辅助可视化:

图形 文章中所述,您可以使用 layout 属性以非常灵活的方式排列图形面板。对于上面的示例,我们在以下 div 中包含了三个可视化:

::: {layout="[ [1], [1,1] ]"}

(输出)

:::

请注意,您可以将 layout 属性应用于已经是面板(例如 .panel-fill)的 div,以指定与侧边栏相邻内容的布局。因此,以下标记也是有效的:

::: {.panel-sidebar}

(输入)

:::

::: {.panel-fill layout="[ [1], [1,1] ]"}

(输出)

:::

layout 属性是一个数组数组,每个数组定义布局的一行。上面我们表示我们希望第一行包含第一个可视化,然后将接下来的两个均等地分布在第二行。

行中的值不需要加起来达到任何特定值(它们在每行内是相对的),因此如果我们更好地呈现数据,我们也可以为第二行指定不同的相对宽度:

::: {layout="[ [1], [3,2] ]"}

(输出)

:::