#SOGL, 64 bytes
be%→M"Q└ƨS‘ač;┼→S%‘A |e3 -* |++M?tMSeM-9*@*a+┼Ot}be÷:?{teSa┼Ot
Explanation:
First function:
→M define function M which pushes
b the book amount
% mod
e the bookshelf width
second function:
→S create function S (example input: 3) [3]
"Q└ƨS‘ push the string "|||XXX|||" (the book) [3, "|||XXX|||"]
* multiply by the number on stack (book count) ["|||XXX||||||XXX||||||XXX|||"]
a push variable A (later defined "|||") ["|||XXX||||||XXX||||||XXX|||", "|||"]
č chop into char array ["|||XXX||||||XXX||||||XXX|||", ["|", "|", "|"]]
; swap top 2 on stack [["|", "|", "|"], "|||XXX||||||XXX||||||XXX|||"]
┼ horizontally append [["||X||X||X|", "||X||X||X|", "||X||X||X|"]]
this function expects a number (book count) on stack and outputs the bookshelfs books
["||X||X||X|",
"||X||X||X|",
"||X||X||X|"]
Further down example given is e=3 (bookshelf width) and b=8 (book amount)
%‘A var A = "|||"
| push "|" ["|"]
e3* push E * 3 ["|", 9]
-* push that many "-"es ["|", "---------"]
|+ append "|" ["|", "---------|"]
+ prepend the "|" ["|---------|"]
this is the bookshelf top/bottom line and always stays on the stack
first part (half-empty bookshelf)
First main part
M? } if the modulo != 0
tM output the bookshelf top/bottom line
S execute the S function width the modulo
eM- push bookshelf width - modulo (empty space count)
9* multiply by 9 (books are 3x3 so 3x3 spaces)
@* get that many spaces
a+ append to that "|||"
┼ horizontally append
O output
t output the bookshelf top/bottom line
And the last part
be÷ floor divide book amout by width (full shelves)
:? if not 0 (a bug makes all loops execute once)
{ repeat
t output the bookshelf top/bottom line
eS execute S with shelf width (full shelf)
a┼ horizontally append "|||"
O output
t output the bookshelf top/bottom line