glänzend 4 kleine textInput-Boxen nebeneinander

77

Ich habe eine glänzende Serverversion 0.4.0 und möchte 4 kleine textInput-Felder haben, die so aussehen:

x-min x-max y-min y-max
[...] [...] [...] [...]

Sie sehen jetzt so aus:

x-min 
[...................]
x-max
[...................]
y-min 
[...................]
y-max 
[...................]

Mit diesem Code:

textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5),
textInput(inputId="ylimitsmin", label="y-min", value = 0.5),
textInput(inputId="ylimitsmax", label="y-max", value = 1.0),

Irgendwelche Ideen, wie dies erreicht werden kann?

EDITIERT: Ich habe solche Dinge an anderer Stelle im Code erfolgreich geändert:

<style type="text/css">select#yaxis4 { height: 280px; width: 500px; }</style>
[... which links to this later on in the page...]
          <label class="control-label" for="yaxis4">Y-Axis</label>
          <select id="yaxis4" multiple="multiple">

Und so sieht es für diejenigen aus, die nicht funktionieren:

<style type="text/css">select#xlimitsmax { display: inline-block; max-width: 50px; }</style>
[... which links to...]
          <label>x-max</label>
          <input id="xlimitsmax" type="text" value="0.5"/>

EDITIERT:

Hier ist ein eigenständiges Beispiel ui.R, das nicht funktioniert:

library(shiny)
shinyUI(
pageWithSidebar(
  # application title
  headerPanel("test01"),
  sidebarPanel(
    tags$head(
      tags$style(type="text/css", "select { max-width: 360px; }"),
      tags$style(type="text/css", ".span4 { max-width: 360px; }"),
      tags$style(type="text/css",  ".well { max-width: 360px; }")
              ),
    wellPanel(
      p(strong("Side Panel:"))
             )
   ),
  mainPanel(
    textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
    tags$head(tags$style(type="text/css", "select#xlimitsmin { max-width: 50px }")),
    textInput(inputId="xlimitsmax", label="x-max", value = 0.5),
    tags$head(tags$style(type="text/css", "select#xlimitsmax { display: inline-block; max-width: 50px; }"))
    )
))

Resultierende Seite:

Geben Sie hier die Bildbeschreibung ein

719016
quelle
Wo im Code sind deine <style>Aussagen? Wenn Sie nur so etwas wie die Farbe des Auswahlfelds ändern, funktioniert das?
John Paul
Die style-Anweisungen sind in der glänzenden Datei ui.R verteilt und werden in den HTML-Kopf ( tags$head(tags$style(type="text/css", "select#something { height: 200px; width: 300px; }"))) eingefügt . Wenn ich so etwas mache, funktioniert es immer noch nicht:tags$head(tags$style(type="text/css", "select#xlimitsmin { max-width: 50px }"))
719016
Klingeln! Kopfgeld in 6 Stunden - können Sie bitte jemandem zuweisen?
Alex Brown
1
Habe ich noch 16 Stunden? Ich werde die Antworten morgen in 12 Stunden im Büro versuchen.
719016

Antworten:

121

Um es zu paraphrasieren (und auf den Fall von 2 Eingaben zu vereinfachen), ist Ihr Problem, dass:

runApp(list(
    ui = bootstrapPage(
        textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
        textInput(inputId="xlimitsmax", label="x-max", value = 0.5)
    ),
    server = function(input, output) {}
))

zeigt an

Geben Sie hier die Bildbeschreibung ein

Aber Sie möchten nebeneinander kleine Eingaben, wie folgt:

kleine Reihe

Die kurze Antwort

textInputRow<-function (inputId, label, value = "") 
{
    div(style="display:inline-block",
        tags$label(label, `for` = inputId), 
        tags$input(id = inputId, type = "text", value = value,class="input-small"))
}
runApp(list(
    ui = bootstrapPage(
        textInputRow(inputId="xlimitsmin", label="x-min", value = 0.0),
        textInputRow(inputId="xlimitsmax", label="x-max", value = 0.5)
    ),
    server = function(input, output) {}
))

gibt:

Geben Sie hier die Bildbeschreibung ein

Die lange Antwort

Side-by-Side-Eingänge

Lassen Sie uns zuerst Seite an Seite arbeiten:

Derzeit generiert textInput zwei separate Tags - das labelund das input, von denen jedes von CSS als konfiguriert wird. Dies display:blockbedeutet, dass es sich um ein Rechteck handelt, das auf der linken Seite des Containers unterbrochen wird . Wir müssen jedes textInputFeld in einen neuen Container (div) einschließen und diesem Container mitteilen, dass sich der darauf folgende Container (der nächste textInput) mithilfe von CSS in derselben horizontalen Zeile auf der Seite befinden darf display:inline-block.

Also fügen wir jedem einen Div mit einem Stil hinzu textInput:

runApp(list(
    ui = bootstrapPage(
        div(style="display:inline-block",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
        div(style="display:inline-block",textInput(inputId="xlimitsmax", label="x-max", value = 0.5))
    ),
    server = function(input, output) {}
))

Reihe

Kleine Eingänge

Jetzt beschäftigen wir uns mit kleinen. Es gibt ein paar Möglichkeiten, klein zu machen,

  1. verkleinern Sie die Schrift,
  2. Stellen Sie sicher, dass das Eingabefeld weniger Zeichen enthält.
  3. Sagen Sie CSS oder (hier) Bootstrap, dass sie eine kleinere Box zeichnen sollen

Da bootstrap.jswir bei der Verwendung von Shiny wirklich die Kontrolle über das Layout haben, funktionieren nur 3 zuverlässig. Lassen Sie uns das verwenden.

Die Eingabegrößen sind in der CSS Forms-Dokumentation von Bootstrap 2.3.2 unter "Control Sizing" dokumentiert . Es enthält eine Vielzahl von Größen von Mini, Klein, Mittel, Groß, Xlarge und Xxlarge. Die Standardeinstellung ist wahrscheinlich Medium. Versuchen wir es stattdessen klein.

Um die Größe festzulegen, müssen wir die Klasse des von inputgenerierten Tags ändern textInput.

Jetzt textInputist nur eine Komfortfunktion um die leistungsstärkeren tagsFunktionen wie tags$labelund tags$input. Wir können eine leistungsfähigere Version davon erstellen textInput, mit der wir die Elemente konfigurieren können, insbesondere die Klasse des inputKnotens:

textInput2<-function (inputId, label, value = "",...) 
{
    tagList(tags$label(label, `for` = inputId), tags$input(id = inputId, 
                                                           type = "text", value = value,...))
}
runApp(list(
    ui = bootstrapPage(
        div(style="display:inline-block",textInput2(inputId="xlimitsmin", label="x-min", value = 0.0, class="input-small")),
        div(style="display:inline-block",textInput2(inputId="xlimitsmax", label="x-max", value = 0.5, class="input-small"))
    ),
    server = function(input, output) {}
))

kleine Reihe

Und wir sind fertig - aber wir können einige dieser Funktionen zusammenfassen, indem wir textInput3das div-Tag generiert haben. Es könnte auch die Klasse selbst festlegen, aber das überlasse ich Ihnen zum Schreiben.

Verpacken

textInput3<-function (inputId, label, value = "",...) 
{
    div(style="display:inline-block",
        tags$label(label, `for` = inputId), 
        tags$input(id = inputId, type = "text", value = value,...))
}
runApp(list(
    ui = bootstrapPage(
        textInput3(inputId="xlimitsmin", label="x-min", value = 0.0, class="input-small"),
        textInput3(inputId="xlimitsmax", label="x-max", value = 0.5, class="input-small")
    ),
    server = function(input, output) {}
))

Aus Gründen des Interesses ist hier die Version mit Klasse input-mini:

Geben Sie hier die Bildbeschreibung ein

Alex Brown
quelle
90

Mit Shiny (> = 0.11) können Sie dies erreichen, indem Sie die Eingabeaufrufe in ein splitLayout () einfügen . Dadurch werden die flüssige Zeile, das Feld usw. in die erforderlichen Spalten aufgeteilt, die erforderlich sind, um Ihre Eingabefelder nebeneinander anzuzeigen.

Das folgende Beispiel gibt Ihnen drei Texteingaben in einem Feld, die nebeneinander in der fluidRow angezeigt werden.

fluidRow(
  box(width = 12, title = "A Box in a Fluid Row I want to Split", 
      splitLayout(
        textInput("inputA", "The first input"),
        textInput("inputB", "The second input"),
        textInput("inputC", "The third input")
      )
  )
)
Nadir Sidi
quelle
17
+1, splitLayout ermöglicht es Ihnen auch, die Breite jeder einzelnen Zelle anzupassen. splitLayout (cellWidths = c ("25%", "75%"), plotOutput ("plot1"), plotOutput ("plot2"))
ZN13
6
Dies sollte jetzt die akzeptierte Antwort sein, genau danach war OP.
Joris Meys
5
Und die Funktion boxist aus dem shinydashboardPaket
NeedRhelp
Dies ist viel einfacher als die akzeptierte Antwort, danke Nadir.
Bob
40

Vielleicht gab es diese Lösung 2013 nicht, aber wenn Sie dies tun möchten, ohne HTML oder CSS zu schreiben, können Sie die columnFunktion einfach fluidRowso verwenden:

  fluidRow(
    column(3,
    selectInput('pcat', 'Primary Category', c("ALL", "Some"))),
    column(3,
    selectInput('smodel', 'Statistical Model', c("NONE", "LINEAR REGRESSION", "LOWESS")))
  )

Und es wird die Dinge nebeneinander stellen.

EDIT: Jetzt gibt es eine andere sehr einfache Möglichkeit, dies mit der splitLayout()Funktion zu tun . Weitere Informationen finden Sie in der Antwort von Nadir Sidi.

CW
quelle
6
Vielen Dank! Dies ersparte mir große Kopfschmerzen. Zum Glück habe ich die Antworten nach unten gescrollt. Dies ist höchstwahrscheinlich der beste Weg, dies zu tun.
Nico
Ich mag diese Lösung auch. splitLayout führt Bildlaufleisten ein, wenn Sie Dropdown-Menüs haben, was es für diese Art von Eingaben ziemlich unzureichend macht. Aber mit Spalte funktioniert das super!
JReddig
8

Ich habe die alte Antwort gelöscht - hier ist eine, die funktioniert:

ui.r:

library(shiny)
shinyUI(
  pageWithSidebar(
  # application title
  headerPanel("test01"),
  sidebarPanel(
     tags$head(
        tags$style(type="text/css", "select { max-width: 360px; }"),
        tags$style(type="text/css", ".span4 { max-width: 360px; }"),
        tags$style(type="text/css",  ".well { max-width: 360px; }")
      ),
     wellPanel(
        p(strong("Side Panel:"))
     )
  ),

 mainPanel(

    div(id="XXmin",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
    tags$head(tags$style(type="text/css", "#XXmin {display: inline-block}")),
    tags$head(tags$style(type="text/css", "#xlimitsmin {max-width: 50px}")),

    div(id="XXmax",textInput(inputId="xlimitsmax", label="x-max", value = 0.5)),
    tags$head(tags$style(type="text/css", "#XXmax {display: inline-block}"),
    tags$head(tags$style(type="text/css", "#xlimitsmax {max-width: 50px}"))

  ))
))

Hier sind die Änderungen, die ich vorgenommen habe:

1) Ich habe das selectaus select#xlimitsmaxund select#xlimitsminin Ihren .cssAussagen beseitigt

2) Ich habe Ihre beiden Steuerelemente jeweils einzeln aufgeführt div()und ihnen die Namen XXminund gegeben XXmax. Ich habe dann hinzugefügt.css Anweisungen , um sie inline-block zu machen.

Wenn Sie eine Reihe von diesen haben, möchten Sie möglicherweise eine classAnweisung verwenden - wie zum Beispiel:

div(class="MyClass",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
tags$head(tags$style(type="text/css", ".MyClass {display: inline-block}")),
tags$head(tags$style(type="text/css", "#xlimitsmin {max-width: 50px}")),

Dann können Sie jedes Steuerelement div()als kennzeichnen class="MyClass"und nur eine .cssAnweisung verwenden.

Bearbeitet, um hinzuzufügen: Vielen Dank, dass Sie den Beispielcode veröffentlicht haben - das hat es viel einfacher gemacht.

2. Bearbeitung: Nur zur Klarstellung. Der Zweck des Einfügens der textInputBefehle in a div()besteht darin, das Eingabefeld und seine Beschriftung in einem einzigen Objekt zu vereinen, damit Stile (in diesem Fall der displayStil) angewendet werden können. Wenn Sie dies nicht tun, fungieren das Etikett und das Feld als zwei separate Einheiten, und es ist in solchen Fällen schwieriger, sie zu manipulieren.

Johannes Paul
quelle
8

Als Alternative zum Einfügen ausführlicher Stildeklarationen in eine Klasse können Sie die Funktionen für glänzende Tags anscheinend problemlos nach Ihren Wünschen erweitern. Dieser spezielle wäre standardmäßig praktisch. (Dies ist mit glänzend glänzend_0.14.1). Ich dachte, ich müsste einen Abschluss schreiben, aber das scheint zu funktionieren.

inline = function (x) {
tags$div(style="display:inline-block;", x)
}

inline(textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
inline(textInput(inputId="xlimitsmax", label="x-max", value = 0.5)),
inline(textInput(inputId="ylimitsmin", label="y-min", value = 0.5)),
inline(textInput(inputId="ylimitsmax", label="y-max", value = 1.0)),
Nathan Siemers
quelle
Ich mag diese Antwort, dies kann hilfreich sein, um andere Probleme zu lösen. Danke
Johan Rosa
Dies war in der Tat die einzige Möglichkeit für mich, eine unerwünschte horizontale Bildlaufleiste loszuwerden. Vielen Dank!
Brunox13
2

Wenn Sie die Eingaben in mainPanel möchten, können Sie Folgendes verwenden:

div(class="row-fluid",
  div(class="span1",textInput("xlimitsmin", label = "x-min", value = 0.0)), 
  div(class="span1",textInput("xlimitsmax", label = "x-max", value = 0.5)),
  div(class="span1",textInput("ylimitsmin", label = "y-min", value = 0.5)),
  div(class="span1",textInput("ylimitsmax", label = "y-max", value = 1.0))
)

Hinzufügen:

#xlimitsmin, #xlimitsmax, #ylimitsmin, #ylimitsmax { 
    max-width: 25px; 
}

in einer CSS-Datei (z. B. style.css im Verzeichnis www /) in Ihrer App und beziehen Sie sie von ui.R mit:

includeCSS ('www / style.R')

Ich bin mir nicht sicher, warum Sie einen textInput anstelle eines numericInput benötigen, da die Eingabe, nach der Sie zu suchen scheinen, numerisch ist. Wenn Sie numericInput wählen, können Sie textInput oben einfach durch numericInput ersetzen. Wenn Sie die Eingaben im sidebarPanel möchten, können Sie den folgenden Code verwenden. Die gleiche CSS-Datei, die oben erwähnt wurde, wäre erforderlich.

div(class="row-fluid",
    div(class="span3",numericInput("xlimitsmin", label = "x-min", value = 0.0)), 
    div(class="span3",numericInput("xlimitsmax", label = "x-max", value = 0.5)),
    div(class="span3",numericInput("ylimitsmin", label = "y-min", value = 0.5)),
    div(class="span3",numericInput("ylimitsmax", label = "y-max", value = 1.0))
)
Vincent
quelle
2

Ich war nicht zufrieden damit, splitLayout()weil es Bildlaufleisten einführt, wenn der Platz begrenzt ist.

Ich fand heraus, dass zumindest für Eingabe-Widgets wie Schaltflächen oder Textfelder eine recht einfache Lösung mit besserem Reaktionsverhalten die Verwendung von Flexboxen ist: (siehe diese großartige Anleitung: https://css-tricks.com/snippets/css/a -guide-to-flexbox / )

div(
  style = "display: flex; flex-wrap: wrap;",
  div(
    style = "flex: 1;",
    textInput("inputA", "The first input")
  ),
  div(
    style = "flex: 1;",
    textInput("inputB", "The second input")
  ),
  div(
    style = "flex: 1;",
    textInput("inputC", "The third input")
  )
)

Es ist möglich, relative Breiten anzupassen. Entspricht splitLayout(cellWidths = c("25%", "75%"), ...):

div(
  style = "display: flex; flex-wrap: wrap;",
  div(
    style = "flex: 1;",
    textInput("inputA", "The first input")
  ),
  div(
    style = "flex: 3;", # second item 3 times as wide as first one
    textInput("inputB", "The second input")
  )
)
sgrubsmyon
quelle
0

Sgrubsmyons Ansatz war für mich fast perfekt, aber ich stieß auf ein neues Problem mit dem Flex-Box-Ansatz, da zwischen den Eingängen keine Auffüllung bestand. Anscheinend hat dies etwas damit zu tun, dass "display: flex" ein Wrapper für "flex-grow 1" ist, der den gesamten verfügbaren Speicherplatz belegt. Ich tauchte in das Kaninchenloch ein und konnte dies nicht zum Laufen bringen, lernte aber einen ähnlichen Ansatz kennen, der "CSS - Grid" verwendet und noch einfacher ist (relevante SO-Frage, aus der ich dies gelernt habe) :

div(
  style = "display: grid; 
          grid-template-columns: 20% repeat(3, 20%); ## same as repeat(4, 20%)
          grid-gap: 10px;",

    textInput("inputA", "The first input"),

    textInput("inputB", "The second input"),

    textInput("inputC", "The third input"),

    textInput("inputD", "The fourth input")

)

Für den hier befindlichen CSS-Grid-Ansatz gibt es eine ähnlich gute Anleitung , in der Sie mehr über die verschiedenen Argumente und Anpassungsmöglichkeiten erfahren, die Sie verwenden können. Beachten Sie, dass ich CSS erst 2 Stunden vor dem Schreiben dieser Antwort berührt habe, daher sind Korrekturen willkommen =)

Maharero
quelle