LP#1424815: 'Read more' accordion in record view
authorJake Litrell <jake@masslnc.org>
Wed, 27 Jul 2016 03:57:10 +0000 (23:57 -0400)
committerKathy Lussier <klussier@masslnc.org>
Fri, 3 Nov 2017 14:27:27 +0000 (10:27 -0400)
First part to add an optional 'read more' clicky to long fields in the
record view.  CSS-based, no javascript involved.

Signed-off-by: Jake Litrell <jake@greanvine.com>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Conflicts:
Open-ILS/src/templates/opac/css/style.css.tt2

Conflicts:
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
Open-ILS/src/templates/opac/css/style.css.tt2

Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
Open-ILS/src/templates/opac/css/style.css.tt2
Open-ILS/src/templates/opac/parts/record/contents.tt2

index c788761..95ae2fd 100644 (file)
@@ -202,6 +202,48 @@ sub init_ro_object_cache {
         return (grep {$_->shortname eq $sn} @$list)[0];
     };
 
+
+
+
+
+    # Turns one string into two for long text strings
+    $locale_subs->{split_for_accordion} = sub {
+        my $string = shift;
+        my $trunc_length = shift;
+
+        return unless defined $string && defined $trunc_length;
+        return if $trunc_length > length $string;
+
+        my $short;
+        my $rest;
+        # find non-whitespace from end
+        # 0-BASED!
+
+        if ((substr $string, $trunc_length, 1) =~ /\s/) {
+            # Next character is whitespace - we're good; split there
+
+            $short = substr $string, 0, $trunc_length;
+            $rest  = substr $string, $trunc_length;
+        }
+        else {
+            # find the space nearest the end of our (not-yet) truncated string
+            my $loc = rindex $string, ' ', $trunc_length;
+
+            # FIXME: if $loc = -1 (can't truncate - no spaces found)
+            $short = substr $string, 0, $loc;
+            $rest = substr $string, $loc;
+        }
+
+        return ($short, $rest);
+    };
+
+
+
+
+
+
+
+
     $locale_subs->{aouct_tree} = sub {
 
         # fetch the org unit tree
@@ -238,7 +280,7 @@ sub init_ro_object_cache {
                     }
                 }
 
-                $cache{aouct_tree}{$locale} = 
+                $cache{aouct_tree}{$locale} =
                     $node_tree->org_unit if $node_tree;
             }
             undef $e;
@@ -330,9 +372,9 @@ sub generic_redirect {
 
     $self->apache->print(
         $self->cgi->redirect(
-            -url => $url || 
-                $self->cgi->param('redirect_to') || 
-                $self->ctx->{referer} || 
+            -url => $url ||
+                $self->cgi->param('redirect_to') ||
+                $self->ctx->{referer} ||
                 $self->ctx->{home_page},
             -cookie => $cookie
         )
@@ -578,7 +620,7 @@ sub _get_pref_lib {
     if ($ctx->{user}) {
         # See if the user has a search library preference
         my $lset = $self->editor->search_actor_user_setting({
-            usr => $ctx->{user}->id, 
+            usr => $ctx->{user}->id,
             name => 'opac.default_search_location'
         })->[0];
         return OpenSRF::Utils::JSON->JSON2perl($lset->value) if $lset;
@@ -630,7 +672,7 @@ sub load_eg_cache_hash {
     }
 }
 
-# Extracts the copy location org unit and group from the 
+# Extracts the copy location org unit and group from the
 # "logc" param, which takes the form org_id:grp_id.
 sub extract_copy_location_group_info {
     my $self = shift;
@@ -647,7 +689,7 @@ sub load_copy_location_groups {
     my $self = shift;
     my $ctx = $self->ctx;
 
-    # User can access to the search location groups at the current 
+    # User can access to the search location groups at the current
     # search lib, the physical location lib, and the patron's home ou.
     my @ctx_orgs = $ctx->{search_ou};
     push(@ctx_orgs, $ctx->{physical_loc}) if $ctx->{physical_loc};
@@ -659,7 +701,7 @@ sub load_copy_location_groups {
             owner => {
                 in => {
                     select => {aou => [{
-                        column => 'id', 
+                        column => 'id',
                         transform => 'actor.org_unit_full_path',
                         result_field => 'id',
                     }]},
@@ -726,7 +768,7 @@ sub load_search_filter_groups {
         if (! ($grps = $cache{search_filter_groups}{$org_id}) ) {
             $grps = $self->editor->search_actor_search_filter_group([
                 {owner => $org_id},
-                {   flesh => 2, 
+                {   flesh => 2,
                     flesh_fields => {
                         asfg => ['entries'],
                         asfge => ['query']
@@ -737,7 +779,7 @@ sub load_search_filter_groups {
             $cache{search_filter_groups}{$org_id} = $grps;
         }
 
-        # for the current context, if a descendant org has a group 
+        # for the current context, if a descendant org has a group
         # with a matching code replace the group from the parent.
         $seen{$_->code} = $_ for @$grps;
     }
@@ -780,7 +822,7 @@ sub load_org_util_funcs {
         if ($depth) {
 
             # find the top-most ctx-org ancestor at the provided depth
-            while ($depth < $ctx_ou->ou_type->depth 
+            while ($depth < $ctx_ou->ou_type->depth
                     and $ctx_ou->id != $test_ou->id) {
                 $ctx_ou = $ctx->{get_aou}->($ctx_ou->parent_ou);
             }
@@ -800,7 +842,7 @@ sub load_org_util_funcs {
         return 0;
     };
 
-    # Returns true if the provided org unit is within the same 
+    # Returns true if the provided org unit is within the same
     # org unit hiding depth-scoped tree as the physical location.
     # Org unit hiding is based on the immutable physical_loc
     # and is not meant to change as search/pref/etc libs change
@@ -813,14 +855,14 @@ sub load_org_util_funcs {
 
         return 1 unless $depth; # 0 or undef
 
-        return $ctx->{org_within_scope}->( 
-            $ctx->{get_aou}->($ploc), 
+        return $ctx->{org_within_scope}->(
+            $ctx->{get_aou}->($ploc),
             $ctx->{get_aou}->($org_id), $depth);
+
     };
 
-    # Evaluates to true if the context org (defaults to get_library) 
-    # is not within the hiding scope.  Also evaluates to true if the 
+    # Evaluates to true if the context org (defaults to get_library)
+    # is not within the hiding scope.  Also evaluates to true if the
     # user's pref_ou is set and it's out of hiding scope.
     # Always evaluates to true when ctx.is_staff
     $ctx->{org_hiding_disabled} = sub {
@@ -833,7 +875,7 @@ sub load_org_util_funcs {
 
         return 1 if !$ctx->{org_within_hiding_scope}->($ctx_org);
 
-        return 1 if $ctx->{pref_ou} and $ctx->{pref_ou} != $ctx_org 
+        return 1 if $ctx->{pref_ou} and $ctx->{pref_ou} != $ctx_org
             and !$ctx->{org_within_hiding_scope}->($ctx->{pref_ou});
 
         return 0;
@@ -841,7 +883,7 @@ sub load_org_util_funcs {
 
 }
 
-# returns the list of org unit IDs for which the 
+# returns the list of org unit IDs for which the
 # selected org unit setting returned a true value
 sub setting_is_true_for_orgs {
     my ($self, $setting) = @_;
@@ -861,7 +903,7 @@ sub setting_is_true_for_orgs {
 }
 
 # Builds and links a perm checking function, testing permissions against
-# the currently logged in user.  
+# the currently logged in user.
 # ctx->{has_perm}->(perm_code, org_id) => 1/undef
 # For security, perm checks are cached per page, not per process.
 sub load_perm_funcs {
@@ -872,7 +914,7 @@ sub load_perm_funcs {
         return 0 unless $self->editor->requestor;
 
         if ($perm_cache{$org_id}) {
-            return $perm_cache{$org_id}{$perm_code} 
+            return $perm_cache{$org_id}{$perm_code}
                 if exists $perm_cache{$org_id}{$perm_code};
         } else {
             $perm_cache{$org_id} = {};
@@ -881,7 +923,7 @@ sub load_perm_funcs {
             $self->editor->allowed($perm_code, $org_id);
     }
 }
-    
+
 
 
 1;
index aade957..248ea43 100644 (file)
@@ -3332,4 +3332,92 @@ label[for*=expert_]
 }
 [id^="toggled-inline-"]:target{
     display: inline;
+ul {
+  list-style: none;
+  perspective: 900;
+  padding: 0;
+  margin: 0;
+}
+ul li {
+  position: relative;
+  padding: 0;
+  margin: 0;
+  padding-bottom: 4px;
+  padding-top: 18px;
+  border-top: 1px dotted #dce7eb;
+}
+ul li:nth-of-type(1) {
+  animation-delay: 0.5s;
+}
+ul li:nth-of-type(2) {
+  animation-delay: 0.75s;
+}
+ul li:nth-of-type(3) {
+  animation-delay: 1s;
+}
+ul li:last-of-type {
+  padding-bottom: 0;
+}
+ul li i {
+  position: absolute;
+  transform: translate(-6px, 0);
+  margin-top: 16px;
+  right: 0;
+}
+ul li i:before, ul li i:after {
+  content: "";
+  position: absolute;
+  background-color: #ff6873;
+  width: 3px;
+  height: 9px;
+}
+ul li i:before {
+  transform: translate(-2px, 0) rotate(45deg);
+}
+ul li i:after {
+  transform: translate(2px, 0) rotate(-45deg);
+}
+ul li input[type=checkbox] {
+  position: absolute;
+  cursor: pointer;
+  width: 10%;
+  height: 100%;
+  z-index: 1;
+  opacity: 0;
+}
+ul li input[type=checkbox]:checked ~ p {
+  margin-top: 0;
+  max-height: 0;
+  opacity: 0;
+  transform: translate(0, 50%);
+}
+ul li input[type=checkbox]:checked ~ i:before {
+  transform: translate(2px, 0) rotate(45deg);
+}
+ul li input[type=checkbox]:checked ~ i:after {
+  transform: translate(-2px, 0) rotate(-45deg);
+}
+
+@keyframes flipdown {
+  0% {
+    opacity: 0;
+    transform-origin: top center;
+    transform: rotateX(-90deg);
+  }
+  5% {
+    opacity: 1;
+  }
+  80% {
+    transform: rotateX(8deg);
+  }
+  83% {
+    transform: rotateX(6deg);
+  }
+  92% {
+    transform: rotateX(-3deg);
+  }
+  100% {
+    transform-origin: top center;
+    transform: rotateX(0deg);
+  }
 }
index 29fc33b..ca85773 100644 (file)
@@ -2,6 +2,7 @@
 contents =  [
     {
         label => l('General Note: '),
+trunc_length => 20,
         xpath => '//*[@tag="500"]'
     }, {
         label => l('With Note: '),
@@ -11,6 +12,7 @@ contents =  [
         xpath => '//*[@tag="502"]'
     }, {
         label => l('Bibliography, etc. Note: '),
+trunc_length => 25,
         xpath => '//*[@tag="504"]'
     }, {
         label => l('Formatted Contents Note: '),
@@ -150,7 +152,7 @@ contents =  [
     }, {
         label => l('Source of Description Note: '),
         xpath => '//*[@tag="588"]'
-    } 
+    }
 ];
 
 BLOCK render_contents;
@@ -171,14 +173,22 @@ BLOCK render_contents;
         END;
         total_contents = all_content.join(" ").replace('\s+$', '');
         %] [% total_contents;
-        IF total_contents.size; "<br/>"; END;
+        IF total_contents.size;
+            IF total_contents.length > cont.trunc_length; # should be ok on undefined... I think.  Just skips it.
+              # need to chop!
+              blah = ctx.split_for_accordion(total_contents, cont.trunc_length);
+              cont.short = blah.0;
+              cont.long  = blah.1;
+            END;
+            '<br/>';
+        END;
         FOREACH link880 IN graphics;
             '<div class="graphic880"' _ link880.dir _ '>';
             link880.value | html;
             '</div>';
         END;
     END;
-END 
+END
 %]
 [%  BLOCK render_all_contents;
     FOREACH cont IN contents;
@@ -187,7 +197,17 @@ END
 -%]
 <tr>
     <td class='rdetail_content_type'>[% cont.label %]</td>
-    <td class='rdetail_content_value' property='keywords'>[% content %]</td>
+    <td class='rdetail_content_value' property='keywords'>
+[% IF cont.short.length %]
+<ul>
+<li>
+    [% cont.short %]... <input type="checkbox" checked>[% l('(click for more)') %] <p>[% cont.long%]</p>
+</li>
+</ul>
+[% ELSE %]
+    [% content %]
+[% END; %]
+</td>
 </tr>
         [%- END; %]
     [%- END; %]
@@ -202,4 +222,4 @@ END
 [%- content_html %]
     </tbody>
 </table>
-[%- END %]
+[%- END %]
\ No newline at end of file