Initial versions from Horace Dynamite

This commit is contained in:
Jay McCarthy 2010-08-17 15:29:28 -06:00
parent 04d3a23e1c
commit 3d43c7e7ac
3 changed files with 263 additions and 74 deletions

View File

@ -152,6 +152,8 @@
(test-process (make-input* (lambda (n) n)) empty) (test-process (make-input* (lambda (n) n)) empty)
empty) empty)
; XXX Do we need to test "input" ?
(test-equal? "text-input" (test-equal? "text-input"
(->cons (test-process (text-input) (list (make-binding:form #"input_0" #"value")))) (->cons (test-process (text-input) (list (make-binding:form #"input_0" #"value"))))
(cons #"input_0" #"value")) (cons #"input_0" #"value"))
@ -208,6 +210,7 @@
(test-process (default #"def" (text-input)) empty) (test-process (default #"def" (text-input)) empty)
#"def") #"def")
; TEXTAREA element
(test-equal? "textarea-input" (test-equal? "textarea-input"
(test-process (textarea-input) (list (make-binding:form #"input_0" #"value"))) (test-process (textarea-input) (list (make-binding:form #"input_0" #"value")))
"value") "value")
@ -224,6 +227,44 @@
(test-display (textarea-input #:cols 80 #:rows 70)) (test-display (textarea-input #:cols 80 #:rows 70))
'((textarea ([name "input_0"] [rows "70"] [cols "80"]) ""))) '((textarea ([name "input_0"] [rows "70"] [cols "80"]) "")))
; BUTTON element
; XXX test-process
(test-equal? "button"
(test-display (button #"button" #"click me"))
'((button ([type "button"]) "click me")))
(test-equal? "button"
(test-display (button #"button" #"click me" #:disabled #t))
'((button ([type "button"] [disabled "true"]) "click me")))
(test-equal? "button"
(test-display (button #"button" #"click me" #:value #"b1"))
'((button ([type "button"] [value "b1"]) "click me")))
(test-equal? "button"
(test-display (button #"button" #"click me" #:disabled #t #:value #"b2"))
'((button ([type "button"] [disabled "true"] [value "b2"]) "click me")))
; IMG elements
; XXX test-process
(test-equal? "img"
(test-display (img #"pic" #"http://h.d.com/1"))
'((img ([alt "pic"] [src "http://h.d.com/1"]))))
(test-equal? "img"
(test-display (img #"pic" #"http://h.d.com/1" #:height 12))
'((img ([alt "pic"] [src "http://h.d.com/1"] [height "12"]))))
(test-equal? "img"
(test-display (img #"pic" #"http://h.d.com/1" #:longdesc #"longer desc"))
'((img ([alt "pic"] [src "http://h.d.com/1"] [longdesc "longer desc"]))))
(test-equal? "img"
(test-display (img #"pic" #"http://h.d.com/1" #:usemap #"#map"))
'((img ([alt "pic"] [src "http://h.d.com/1"] [usemap "#map"]))))
(test-equal? "img"
(test-display (img #"pic" #"http://h.d.com/1" #:width 50))
'((img ([alt "pic"] [src "http://h.d.com/1"] [width "50"]))))
(test-equal? "img"
(test-display (img #"pic" #"http://h.d.com/1" #:height 12 #:longdesc #"longer desc" #:usemap #"#map" #:width 50))
'((img ([alt "pic"] [src "http://h.d.com/1"] [height "12"] [longdesc "longer desc"] [usemap "#map"] [width "50"]))))
(test-equal? "to-string" (test-equal? "to-string"
(test-process (to-string (required (text-input))) (list (make-binding:form #"input_0" #"value"))) (test-process (to-string (required (text-input))) (list (make-binding:form #"input_0" #"value")))
"value") "value")

231
collects/web-server/formlets/input.rkt Normal file → Executable file
View File

@ -9,6 +9,7 @@
; Low-level ; Low-level
(define (next-name i) (define (next-name i)
(values (format "input_~a" i) (add1 i))) (values (format "input_~a" i) (add1 i)))
(define (make-input* render) (define (make-input* render)
(lambda (i) (lambda (i)
(let-values ([(w i) (next-name i)]) (let-values ([(w i) (next-name i)])
@ -19,6 +20,7 @@
#:when (bytes=? wb (binding-id b))) #:when (bytes=? wb (binding-id b)))
b)) b))
i)))) i))))
(define (make-input render) (define (make-input render)
(lambda (i) (lambda (i)
(let-values ([(w i) (next-name i)]) (let-values ([(w i) (next-name i)])
@ -47,9 +49,10 @@
#;[binding:form/default (bytes? . -> . (formlet/c (binding? . -> . bytes?)))]) #;[binding:form/default (bytes? . -> . (formlet/c (binding? . -> . bytes?)))])
; HTML Spec ; HTML Spec
(define (text-or-password (define (input
#:password? password? #:type [type "text"]
#:value [value #f] #:value [value #f]
#:name [name #f]
#:size [size #f] #:size [size #f]
#:max-length [max-length #f] #:max-length [max-length #f]
#:read-only? [read-only? #f] #:read-only? [read-only? #f]
@ -58,11 +61,11 @@
(lambda (n) (lambda (n)
(list 'input (list 'input
(list* (list 'name n) (list* (list 'name n)
(list 'type (list 'type type)
(if password? "password" "text"))
(append (append
(filter list? (filter list?
(list (and value (list 'value (bytes->string/utf-8 value))) (list (and value (list 'value (bytes->string/utf-8 value)))
(and name (list 'name (bytes->string/utf-8 name)))
(and size (list 'size (number->string size))) (and size (list 'size (number->string size)))
(and max-length (list 'maxlength (number->string max-length))) (and max-length (list 'maxlength (number->string max-length)))
(and read-only? (list 'readonly "true")))) (and read-only? (list 'readonly "true"))))
@ -70,13 +73,15 @@
(define (text-input (define (text-input
#:value [value #f] #:value [value #f]
#:name [name #f]
#:size [size #f] #:size [size #f]
#:max-length [max-length #f] #:max-length [max-length #f]
#:read-only? [read-only? #f] #:read-only? [read-only? #f]
#:attributes [attrs empty]) #:attributes [attrs empty])
(text-or-password (input
#:password? #f #:type "text"
#:value value #:value value
#:name name
#:size size #:size size
#:max-length max-length #:max-length max-length
#:read-only? read-only? #:read-only? read-only?
@ -84,42 +89,104 @@
(define (password-input (define (password-input
#:value [value #f] #:value [value #f]
#:name [name #f]
#:size [size #f] #:size [size #f]
#:max-length [max-length #f] #:max-length [max-length #f]
#:read-only? [read-only? #f] #:read-only? [read-only? #f]
#:attributes [attrs empty]) #:attributes [attrs empty])
(text-or-password (input
#:password? #t #:type "password"
#:value value #:value value
#:name name
#:size size #:size size
#:max-length max-length #:max-length max-length
#:read-only? read-only? #:read-only? read-only?
#:attributes attrs)) #:attributes attrs))
(define (checkbox value checked? (define (checkbox value checked?
#:name [name #f]
#:attributes [attrs empty]) #:attributes [attrs empty])
(input
#:type "checkbox"
#:value value
#:name name
#:attributes
(if checked? (append (list (list 'checked "true")) attrs) attrs)))
(define (radio value checked?
#:name [name #f]
#:attributes [attrs empty])
(input
#:type "radio"
#:name name
#:attributes
(if checked? (append (list (list 'checked "true")) attrs) attrs)))
(define (submit value
#:name [name #f]
#:attributes [attrs empty])
(input
#:type "submit"
#:name name
#:value value
#:attributes attrs))
(define (reset value
#:name [name #f]
#:attributes [attrs empty])
(input
#:type "reset"
#:name name
#:value value
#:attributes attrs))
(define (file-upload #:name [name #f]
#:attributes [attrs empty])
(input
#:type "file"
#:name name
#:attributes attrs))
(define (hidden #:name [name #f]
#:attributes [attrs empty])
(input
#:type "hidden"
#:name name
#:attributes attrs))
(define (button type name
#:disabled [disabled #f]
#:value [value #f]
#:attributes [attrs empty])
(make-input (make-input
(lambda (n) (λ (n)
(list 'input (list 'button
(list* (list 'name n) (list* (list 'type (bytes->string/utf-8 type))
(list 'type "checkbox") (append
(list 'value (bytes->string/utf-8 value)) (filter list?
(append (if checked? (list (list 'checked "true")) empty) (list (and disabled (list 'disabled disabled))
attrs)))))) (and value (list 'value (bytes->string/utf-8 value)))))
attrs))
(bytes->string/utf-8 name)))))
; XXX radio (define (img alt src
#:height [height #f]
; XXX submit #:longdesc [ldesc #f]
#:usemap [map #f]
; XXX reset #:width [width #f]
#:attributes [attrs empty])
; XXX file (make-input
(λ (n)
; XXX hidden (list 'img
(list* (list 'src (bytes->string/utf-8 src))
; XXX image (list 'alt (bytes->string/utf-8 alt))
(append
; XXX button (filter list?
(list (and height (list 'height (number->string height)))
(and ldesc (list 'longdesc (bytes->string/utf-8 ldesc)))
(and map (list 'usemap (bytes->string/utf-8 map)))
(and width (list 'width (number->string width)))))
attrs))))))
(define (multiselect-input l (define (multiselect-input l
#:multiple? [multiple? #t] #:multiple? [multiple? #t]
@ -167,54 +234,78 @@
(define (textarea-input (define (textarea-input
#:rows [rows #f] #:rows [rows #f]
#:cols [cols #f]) #:cols [cols #f])
(to-string (make-input
(required (lambda (n)
(make-input (list 'textarea
(lambda (n) (list* (list 'name n)
(list 'textarea (append
(list* (list 'name n) (filter list?
(append (list (and rows (list 'rows (number->string rows)))
(filter list? (and cols (list 'cols (number->string cols)))))))
(list (and rows (list 'rows (number->string rows))) ""))))
(and cols (list 'cols (number->string cols)))))))
""))))))
(provide/contract (provide/contract
[multiselect-input (->* (sequence?) [checkbox ((bytes? boolean?)
(#:multiple? boolean? (#:name (or/c false/c bytes?)
#:selected? (any/c . -> . boolean?) #:attributes (listof (list/c symbol? string?)))
#:display (any/c . -> . pretty-xexpr/c)) . ->* .
(formlet/c (listof any/c)))] (formlet/c (or/c false/c binding?)))]
[select-input (->* (sequence?) [radio ((bytes? boolean?)
(#:selected? (any/c . -> . boolean?) (#:name (or/c false/c bytes?)
#:display (any/c . -> . pretty-xexpr/c)) #:attributes (listof (list/c symbol? string?)))
(formlet/c any/c))] . ->* .
(formlet/c (or/c false/c binding?)))]
[submit ((bytes?)
(#:name (or/c false/c bytes?)
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[reset ((bytes?)
(#:name (or/c false/c bytes?)
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[file-upload (()
(#:name (or/c false/c bytes?)
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[hidden (()
(#:name (or/c false/c bytes?)
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[img ((bytes? bytes?)
(#:height (or/c false/c exact-nonnegative-integer?)
#:longdesc (or/c false/c bytes?)
#:usemap (or/c false/c bytes?)
#:width (or/c false/c exact-nonnegative-integer?)
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c string?))]
[button ((bytes? bytes?)
(#:disabled boolean?
#:value (or/c false/c bytes?)
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[multiselect-input ((sequence?)
(#:multiple? boolean?
#:selected? (any/c . -> . boolean?)
#:display (any/c . -> . pretty-xexpr/c))
. ->* .
(formlet/c (listof any/c)))]
[select-input ((sequence?)
(#:selected? (any/c . -> . boolean?)
#:display (any/c . -> . pretty-xexpr/c))
. ->* .
(formlet/c any/c))]
[textarea-input (() [textarea-input (()
(#:rows number? (#:rows number?
#:cols number?) #:cols number?)
. ->* . . ->* .
(formlet/c string?))] (formlet/c string?))])
[text-input (()
(#:value (or/c false/c bytes?)
#:size (or/c false/c exact-nonnegative-integer?)
#:max-length (or/c false/c exact-nonnegative-integer?)
#:read-only? boolean?
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[password-input (()
(#:value (or/c false/c bytes?)
#:size (or/c false/c exact-nonnegative-integer?)
#:max-length (or/c false/c exact-nonnegative-integer?)
#:read-only? boolean?
#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))]
[checkbox ((bytes? boolean?)
(#:attributes (listof (list/c symbol? string?)))
. ->* .
(formlet/c (or/c false/c binding?)))])
; High-level ; High-level
(define (required f) (define (required f)

65
collects/web-server/scribblings/formlets.scrbl Normal file → Executable file
View File

@ -277,7 +277,7 @@ These @tech{formlet}s are the main combinators for form input.
(formlet/c (or/c false/c binding?))]{ (formlet/c (or/c false/c binding?))]{
This @tech{formlet} is rendered with @racket[render], which is passed the input name, and results in the This @tech{formlet} is rendered with @racket[render], which is passed the input name, and results in the
extracted @racket[binding]. extracted @racket[binding].
} }
@defproc[(make-input* [render (string? . -> . xexpr/c)]) @defproc[(make-input* [render (string? . -> . xexpr/c)])
(formlet/c (listof binding?))]{ (formlet/c (listof binding?))]{
@ -286,6 +286,7 @@ These @tech{formlet}s are the main combinators for form input.
} }
@defproc[(text-input [#:value value (or/c false/c bytes?) #f] @defproc[(text-input [#:value value (or/c false/c bytes?) #f]
[#:name name (or/c false/c bytes?) #f]
[#:size size (or/c false/c exact-nonnegative-integer?) #f] [#:size size (or/c false/c exact-nonnegative-integer?) #f]
[#:max-length max-length (or/c false/c exact-nonnegative-integer?) #f] [#:max-length max-length (or/c false/c exact-nonnegative-integer?) #f]
[#:read-only? read-only? boolean? #f] [#:read-only? read-only? boolean? #f]
@ -295,6 +296,7 @@ These @tech{formlet}s are the main combinators for form input.
} }
@defproc[(password-input [#:value value (or/c false/c bytes?) #f] @defproc[(password-input [#:value value (or/c false/c bytes?) #f]
[#:name name (or/c false/c bytes?) #f]
[#:size size (or/c false/c exact-nonnegative-integer?) #f] [#:size size (or/c false/c exact-nonnegative-integer?) #f]
[#:max-length max-length (or/c false/c exact-nonnegative-integer?) #f] [#:max-length max-length (or/c false/c exact-nonnegative-integer?) #f]
[#:read-only? read-only? boolean? #f] [#:read-only? read-only? boolean? #f]
@ -302,20 +304,75 @@ These @tech{formlet}s are the main combinators for form input.
(formlet/c (or/c false/c binding?))]{ (formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using an INPUT element with the PASSWORD type and the attributes given in the arguments. This @tech{formlet} renders using an INPUT element with the PASSWORD type and the attributes given in the arguments.
} }
@defproc[(textarea-input [#:rows rows (or/c false/c number?) #f] @defproc[(textarea-input [#:rows rows (or/c false/c number?) #f]
[#:cols cols (or/c false/c number?) #f]) [#:cols cols (or/c false/c number?) #f])
(formlet/c string?)]{ (formlet/c string?)]{
This @tech{formlet} renders using an TEXTAREA element. This @tech{formlet} renders using an TEXTAREA element with attributes given in the arguments.
} }
@defproc[(checkbox [value bytes?] @defproc[(checkbox [value bytes?]
[checked? boolean?] [checked? boolean?]
[#:name name (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty]) [#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{ (formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using a INPUT elemen with the CHECKBOX type and the attributes given in the arguments. This @tech{formlet} renders using an INPUT element with the CHECKBOX type and the attributes given in the arguments.
} }
@defproc[(radio [value bytes?]
[checked? boolean?]
[#:name name (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using an INPUT element with the RADIO type and the attributes given in the arguments.
}
@defproc[(submit [value bytes?]
[#:name name (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using an INPUT element with the SUBMIT type and the attributes given in the arguments.
}
@defproc[(reset [value bytes?]
[#:name name (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using an INPUT element with the RESET type and the attributes given in the arguments.
}
@defproc[(file-upload [#:name name (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using an INPUT element with the FILE type and the attributes given in the arguments.
}
@defproc[(hidden [#:name name (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using an INPUT element with HIDDEN type and the attributes given in the arguments.
}
@defproc[(img [alt bytes?]
[src bytes?]
[#:height height (or/c false/c exact-nonnegative-integer?) #f]
[#:longdesc ldesc (or/c false/c bytes?) #f]
[#:usemap map (or/c false/c bytes?) #f]
[#:width width (or/c false/c exact-nonnegative-integer?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c string?)]{
This @tech{formlet} renders using an IMG element with the attributes given in the arguments.
}
@defproc[(button [type bytes?]
[button-text bytes?]
[#:disabled disabled boolean? #f]
[#:value value (or/c false/c bytes?) #f]
[#:attributes attrs (listof (list/c symbol? string?)) empty])
(formlet/c (or/c false/c binding?))]{
This @tech{formlet} renders using a BUTTON element with the attributes given in the arguments. @racket[button-text] is the text that will appear on the button when rendered.
}
@defproc[(multiselect-input [l sequence?] @defproc[(multiselect-input [l sequence?]
[#:multiple? multiple? boolean? #t] [#:multiple? multiple? boolean? #t]
[#:selected? selected? (any/c . -> . boolean?) (λ (x) #f)] [#:selected? selected? (any/c . -> . boolean?) (λ (x) #f)]